转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8744943

记得我之前参与开发过一个华为的项目,要求程序可以支持好几种终端设备,其中就包括Android手机和Android Pad。然后为了节省人力,公司无节操地让Android手机和Android Pad都由我们团队开发。当时项目组定的方案是,制作两个版本的App,一个手机版,一个Pad版。由于当时手机版的主体功能已经做的差不多了,所以Pad版基本上就是把手机版的代码完全拷过来,然后再根据平板的特性部分稍作修改就好了。

但是,从此以后我们就非常苦逼了。每次要添加什么新功能,同样的代码要写两遍。每次要修复任何bug,都要在手机版代码和Pad版代码里各修改一遍。这还不算什么,每到出版本的时候就更离谱了。华为要求每次需要出两个版本,一个华为内网环境的版本,一个客户现场的版本,而现在又分了手机和Pad,也就是每次需要出四个版本。如果在出完版本后自测还出现了问题,就可以直接通宵了。这尤其是苦了我们的X总(由于他dota打的比较好,我都喜欢叫他X神)。他在我们项目组中单独维护一个模块,并且每次打版本都是由他负责,加班的时候我们都能跑,就是他跑不了。这里也是赞扬一下我们X神的敬业精神,如果他看得到的话。

经历过那么苦逼时期的我也就开始思考,可不可以制作同时兼容手机和平板的App呢?答案当然是肯定的,不过我这个人比较懒,一直也提不起精神去钻研这个问题。直到我一个在美国留学的朋友Gong让我帮她解决她的研究生导师布置的作业(我知道你研究生导师看不懂中文 ^-^),正好涉及到了这一块,也就借此机会研究了一下,现在拿出来跟大家分享。

我们先来看一下Android手机的设置界面,点击一下Sound,可以跳转到声音设置界面,如下面两张图所示:

           

然后再来看一下Android Pad的设置界面,主设置页面和声音设置页面都是在一个界面显示的,如下图所示:

如果这分别是两个不同的App做出的效果,那没有丝毫惊奇之处。但如果是同一个App,在手机上和平板上运行分别有以上两种效果的话,你是不是就已经心动了?我们现在就来模拟实现一下。

首先你需要对Fragment有一定的了解,如果你还没接触过Fragment,建议可以先阅读 Android Fragment完全解析,关于碎片你所需知道的一切 这篇文章。并且本次的代码是运行在Android 4.0版本上的,如果你的SDK版本还比较低的话,建议可以先升升级了。

新建一个Android项目,取名叫FragmentDemo。打开或新建MainActivity作为程序的主Activity,里面有如下自动生成的内容:

  1. public class MainActivity extends Activity {
  2. @Override
  3. public void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. }
  7. }

作为一个Android老手,上面的代码实在太小儿科了,每个Activity中都会有这样的代码。不过今天我们的程序可不会这么简单,加载布局这一块还是大有文章的。

打开或新建res/layout/activity_main.xml作为程序的主布局文件,里面代码如下:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="horizontal"
  6. tools:context=".MainActivity" >
  7. <fragment
  8. android:id="@+id/menu_fragment"
  9. android:name="com.example.fragmentdemo.MenuFragment"
  10. android:layout_width="fill_parent"
  11. android:layout_height="fill_parent"
  12. />
  13. </LinearLayout>

这个布局引用了一个MenuFragment,我们稍后来进行实现,先来看一下今天的一个重点,我们需要再新建一个activity_main.xml,这个布局文件名和前面的主布局文件名是一样的,但是要放在不同的目录下面。

在res目录下新建layout-large目录,然后这个目录下创建新的activity_main.xml,加入如下代码:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="horizontal"
  6. android:baselineAligned="false"
  7. tools:context=".MainActivity"
  8. >
  9. <fragment
  10. android:id="@+id/left_fragment"
  11. android:name="com.example.fragmentdemo.MenuFragment"
  12. android:layout_width="0dip"
  13. android:layout_height="fill_parent"
  14. android:layout_weight="1"
  15. />
  16. <FrameLayout
  17. android:id="@+id/details_layout"
  18. android:layout_width="0dip"
  19. android:layout_height="fill_parent"
  20. android:layout_weight="3"
  21. ></FrameLayout>
  22. </LinearLayout>

这个布局同样也引用了MenuFragment,另外还加入了一个FrameLayout用于显示详细内容。其实也就是分别对应了平板界面上的左侧布局和右侧布局。

这里用到了动态加载布局的技巧,首先Activity中调用 setContentView(R.layout.activity_main) ,表明当前的Activity想加载activity_main这个布局文件。而Android系统又会根据当前的运行环境判断程序是否运行在大屏幕设备上,如果运行在大屏幕设备上,就加载layout-large目录下的activity_main.xml,否则就默认加载layout目录下的activity_main.xml。

关于动态加载布局的更多内容,可以阅读 Android官方提供的支持不同屏幕大小的全部方法 这篇文章。

下面我们来实现久违的MenuFragment,新建一个MenuFragment类继承自Fragment,具体代码如下:

  1. public class MenuFragment extends Fragment implements OnItemClickListener {
  2. /**
  3. * 菜单界面中只包含了一个ListView。
  4. */
  5. private ListView menuList;
  6. /**
  7. * ListView的适配器。
  8. */
  9. private ArrayAdapter<String> adapter;
  10. /**
  11. * 用于填充ListView的数据,这里就简单只用了两条数据。
  12. */
  13. private String[] menuItems = { "Sound", "Display" };
  14. /**
  15. * 是否是双页模式。如果一个Activity中包含了两个Fragment,就是双页模式。
  16. */
  17. private boolean isTwoPane;
  18. /**
  19. * 当Activity和Fragment建立关联时,初始化适配器中的数据。
  20. */
  21. @Override
  22. public void onAttach(Activity activity) {
  23. super.onAttach(activity);
  24. adapter = new ArrayAdapter<String>(activity, android.R.layout.simple_list_item_1, menuItems);
  25. }
  26. /**
  27. * 加载menu_fragment布局文件,为ListView绑定了适配器,并设置了监听事件。
  28. */
  29. @Override
  30. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  31. View view = inflater.inflate(R.layout.menu_fragment, container, false);
  32. menuList = (ListView) view.findViewById(R.id.menu_list);
  33. menuList.setAdapter(adapter);
  34. menuList.setOnItemClickListener(this);
  35. return view;
  36. }
  37. /**
  38. * 当Activity创建完毕后,尝试获取一下布局文件中是否有details_layout这个元素,如果有说明当前
  39. * 是双页模式,如果没有说明当前是单页模式。
  40. */
  41. @Override
  42. public void onActivityCreated(Bundle savedInstanceState) {
  43. super.onActivityCreated(savedInstanceState);
  44. if (getActivity().findViewById(R.id.details_layout) != null) {
  45. isTwoPane = true;
  46. } else {
  47. isTwoPane = false;
  48. }
  49. }
  50. /**
  51. * 处理ListView的点击事件,会根据当前是否是双页模式进行判断。如果是双页模式,则会动态添加Fragment。
  52. * 如果不是双页模式,则会打开新的Activity。
  53. */
  54. @Override
  55. public void onItemClick(AdapterView<?> arg0, View view, int index, long arg3) {
  56. if (isTwoPane) {
  57. Fragment fragment = null;
  58. if (index == 0) {
  59. fragment = new SoundFragment();
  60. } else if (index == 1) {
  61. fragment = new DisplayFragment();
  62. }
  63. getFragmentManager().beginTransaction().replace(R.id.details_layout, fragment).commit();
  64. } else {
  65. Intent intent = null;
  66. if (index == 0) {
  67. intent = new Intent(getActivity(), SoundActivity.class);
  68. } else if (index == 1) {
  69. intent = new Intent(getActivity(), DisplayActivity.class);
  70. }
  71. startActivity(intent);
  72. }
  73. }
  74. }

这个类的代码并不长,我简单的说明一下。在onCreateView方法中加载了menu_fragment这个布局,这个布局里面包含了一个ListView,然后我们对这个ListView填充了两个简单的数据 "Sound" 和 "Display" 。又在onActivityCreated方法中做了一个判断,如果Activity的布局中包含了details_layout这个元素,那么当前就是双页模式,否则就是单页模式。onItemClick方法则处理了ListView的点击事件,发现如果当前是双页模式,就动态往details_layout中添加Fragment,如果当前是单页模式,就直接打开新的Activity。

我们把MenuFragment中引用到的其它内容一个个添加进来。新建menu_fragment.xml文件,加入如下代码:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent" >
  5. <ListView
  6. android:id="@+id/menu_list"
  7. android:layout_width="fill_parent"
  8. android:layout_height="fill_parent"
  9. ></ListView>
  10. </LinearLayout>

然后新建SoundFragment,里面内容非常简单:

  1. public class SoundFragment extends Fragment {
  2. @Override
  3. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  4. View view = inflater.inflate(R.layout.sound_fragment, container, false);
  5. return view;
  6. }
  7. }

这里SoundFragment需要用到sound_fragment.xml布局文件,因此这里我们新建这个布局文件,并加入如下代码:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:background="#00ff00"
  6. android:orientation="vertical" >
  7. <TextView
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:layout_centerInParent="true"
  11. android:textSize="28sp"
  12. android:textColor="#000000"
  13. android:text="This is sound view"
  14. />
  15. </RelativeLayout>

同样的道理,我们再新建DisplayFragment和display_fragment.xml布局文件:

  1. public class DisplayFragment extends Fragment {
  2. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  3. View view = inflater.inflate(R.layout.display_fragment, container, false);
  4. return view;
  5. }
  6. }
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:background="#0000ff"
  6. android:orientation="vertical" >
  7. <TextView
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:layout_centerInParent="true"
  11. android:textSize="28sp"
  12. android:textColor="#000000"
  13. android:text="This is display view"
  14. />
  15. </RelativeLayout>

然后新建SoundActivity,代码如下:

  1. public class SoundActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.sound_activity);
  6. }
  7. }

这个Activity只是加载了一个布局文件,现在我们来实现sound_activity.xml这个布局文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <fragment xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/sound_fragment"
  4. android:name="com.example.fragmentdemo.SoundFragment"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent" >
  7. </fragment>

这个布局文件引用了SoundFragment,这样写的好处就是,以后我们只需要在SoundFragment中修改代码,SoundActivity就会跟着自动改变了,因为它所有的代码都是从SoundFragment中引用过来的。

好,同样的方法,我们再完成DisplayActivity:

  1. public class DisplayActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.display_activity);
  6. }
  7. }

然后加入display_activity.xml:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <fragment xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/display_fragment"
  4. android:name="com.example.fragmentdemo.DisplayFragment"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent" >
  7. </fragment>

现在所有的代码就都已经完成了,我们来看一下效果吧。

首先将程序运行在手机上,效果图如下:

分别点击Sound和Display,界面会跳转到声音和显示界面:

           

然后将程序在平板上运行,点击Sound,效果图如下:

然后点击Display切换到显示界面,效果图如下:

这样我们就成功地让程序同时兼容手机和平板了。当然,这只是一个简单的demo,更多复杂的内容需要大家自己去实现了。

好了,今天的讲解到此结束,有疑问的朋友请在下面留言。

源码下载,请点击这里

Android手机平板两不误,使用Fragment实现兼容手机和平板的程序的更多相关文章

  1. 【Android 界面效果17】Android手机平板两不误,使用Fragment实现兼容手机和平板的程序

    记得我之前参与开发过一个华为的项目,要求程序可以支持好几种终端设备,其中就包括Android手机和Android Pad.然后为了节省人力,公司无节操地让Android手机和Android Pad都由 ...

  2. Fragment实现兼容手机和平板

    Android手机的设置界面,点击一下Sound,可以跳转到声音设置界面,如下面两张图所示:             然后再来看一下Android Pad的设置界面,主设置页面和声音设置页面都是在一个 ...

  3. android studio 2.2.2下fragment的创建和跳转

    一,首先,Fragment是android应用中十分重要的一个功能,十分轻量化,也类似于activity一样,是一个个布局,可以相互跳转和传递参数.但是,它运行起来十分流畅,而且易于管理,下面是在学习 ...

  4. Android开发之漫漫长途 XII——Fragment详解

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  5. Android开发之漫漫长途 XIII——Fragment最佳实践

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  6. ADB连接手机的两种方式(usb数据线连接和wifi连接)

    ADB(Android Debug Bridge)安卓测试桥,它是连接电脑开发端和安卓设备的桥梁,这个安卓设备可以是真实的安卓手机或者平板,也可以是虚拟的安卓模拟器,   这里介绍ADB连接手机的两种 ...

  7. (转)android Fragments详解三:实现Fragment的界面

    为fragment添加用户界面 fragment一般作为activity的用户界面的一部分,把它自己的layout嵌入到activity的layout中.    一个 要为fragment提供layo ...

  8. [置顶] 新修改ADB,支持Android 4.2 系统 ,全部中文命令,手机屏幕截图等等

    发过好几个ADB的工具,有很多朋友用了之后给我反馈了不少的意见和bug,这里非常感谢他们,所以今天花了一天的时间重新整理了一下ADB,并且修改了这些BUG.也有朋友建议我给一个修改列表,今天发这个帖子 ...

  9. 论山寨手机与Android联姻 【8】 自己动手做XP手机

    2010年1月20日,ViewSonic在北京发布了一款真正意义的电脑手机VCP08.根据商家的宣传,VCP08之所以能够被称为真正的电脑手机,是因为“该机做到了把真正的WindowsXP操作系统嵌入 ...

随机推荐

  1. ZOJ 1002 Fire Net

    题目大意:有一个4*4的城市,其中一些格子有墙(X表示墙),在剩余的区域放置碉堡.子弹不能穿透墙壁.问最多可以放置几个碉堡,保证它们不会相互误伤. 解法:从左上的顶点开始遍历,如果这个点不是墙,做深度 ...

  2. C++ Primer : 第九章 : vector变长、string的其他操作以及容器适配器

    vector变长机制.string的其他构造方法,添加.替换和搜索操作,string比较和数值转换,最后是容器适配器. vector对象是如何增长的 vector和string类型提供了一些成员函数, ...

  3. 故障模块名称: mso.dll

    本人今天早上打开word文档的时候打不开了,反复试了n次也不成,一想八成儿要重新装了,结果我点开详细信息看了一下,看到了“故障模块名称: mso.dll”这个提示,结果我就放到了百度上找了一下,都是只 ...

  4. Codeforces Round #124 (Div. 2)

    A. Plate Game 如果可以放置一个圆的情况下,先手将圆放置在矩形正中心,那么根据对称性,先手只要放后手的对称的位置即可,也就是先手必胜,否则后手胜. B. Limit 讨论\(n,m\)的大 ...

  5. FZU-2216 The Longest Straight (二分枚举)

    题目大意:给n个0~m之间的数,如果是0,那么0可以变为任意的一个1~m之间的一个数.从中选出若干个数,使构成一个连续的序列.问能构成的最长序列的长度为多少? 题目分析:枚举连续序列的起点,二分枚举二 ...

  6. Visual Studio 2012 离线升级包的制作

    通过CMD执行“VS2012.3.exe/layout”命令,就可以把在线升级的文件保存到本地,这样一来,只要把下载出来的文件打包上传,然后分享出去,所谓的“VS2012 Update 3 离线升级包 ...

  7. 论文笔记之:Multiple Object Recognition With Visual Attention

     Multiple Object Recognition With Visual Attention Google DeepMind  ICRL 2015 本文提出了一种基于 attention 的用 ...

  8. feature visualization from ipython notebook

    Feature visualization from ipython notebook Wang Xiao 1. install anaconda2 from: https://www.continu ...

  9. C#配置升级

    void ConvertProject() { List<BaseProjectConverter> convertors = new List<BaseProjectConvert ...

  10. 尾数为0零BigDecimal不能装成正常数

    BigDecimal b1 = rs.getBigDecimal("binary_double_column"); System.out.println( "ceshi: ...