Android Fragment完全解析及:: Android手机平板使用Fragment实现兼容手机和平板的程序

发布时间:2016-12-8 0:22:27 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"Android Fragment完全解析及:: Android手机平板使用Fragment实现兼容手机和平板的程序",主要涉及到Android Fragment完全解析及:: Android手机平板使用Fragment实现兼容手机和平板的程序方面的内容,对于Android Fragment完全解析及:: Android手机平板使用Fragment实现兼容手机和平板的程序感兴趣的同学可以参考一下。

转载:http://blog.csdn.net/guolin_blog/article/details/8881711 我们都知道,Android上的界面展示都是通过Activity实现的,Activity实在是太常用了,我相信大家都已经非常熟悉了,这里就不再赘述。 但是Activity也有它的局限性,同样的界面在手机上显示可能很好看,在平板上就未必了,因为平板的屏幕非常大,手机的界面放在平板上可能会有过分被拉长、控件间距过大等情况。这个时候更好的体验效果是在Activity中嵌入"小Activity",然后每个"小Activity"又可以拥有自己的布局。因此,我们今天的主角Fragment登场了。 Fragment初探 为了让界面可以在平板上更好地展示,Android在3.0版本引入了Fragment(碎片)功能,它非常类似于Activity,可以像Activity一样包含布局。Fragment通常是嵌套在Activity中使用的,现在想象这种场景:有两个Fragment,Fragment 1包含了一个ListView,每行显示一本书的标题。Fragment 2包含了TextView和ImageView,来显示书的详细内容和图片。 如果现在程序运行竖屏模式的平板或手机上,Fragment 1可能嵌入在一个Activity中,而Fragment 2可能嵌入在另一个Activity中,如下图所示: 而如果现在程序运行在横屏模式的平板上,两个Fragment就可以嵌入在同一个Activity中了,如下图所示: 由此可以看出,使用Fragment可以让我们更加充分地利用平板的屏幕空间,下面我们一起来探究下如何使用Fragment。 首先需要注意,Fragment是在3.0版本引入的,如果你使用的是3.0之前的系统,需要先导入android-support-v4的jar包才能使用Fragment功能。 新建一个项目叫做Fragments,然后在layout文件夹下新建一个名为fragment1.xml的布局文件: [html] view plaincopy <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:layout_width="match_parent"       android:layout_height="match_parent"       android:background="#00ff00" >          <TextView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:text="This is fragment 1"           android:textColor="#000000"           android:textSize="25sp" />      </LinearLayout>   可以看到,这个布局文件非常简单,只有一个LinearLayout,里面加入了一个TextView。我们如法炮制再新建一个fragment2.xml : [html] view plaincopy <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:layout_width="match_parent"       android:layout_height="match_parent"       android:background="#ffff00" >          <TextView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:text="This is fragment 2"           android:textColor="#000000"           android:textSize="25sp" />      </LinearLayout>   然后新建一个类Fragment1,这个类是继承自Fragment的: [java] view plaincopy public class Fragment1 extends Fragment {          @Override       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {           return inflater.inflate(R.layout.fragment1, container, false);       }      }   我们可以看到,这个类也非常简单,主要就是加载了我们刚刚写好的fragment1.xml布局文件并返回。同样的方法,我们再写好Fragment2 : [java] view plaincopy public class Fragment2 extends Fragment {          @Override       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {           return inflater.inflate(R.layout.fragment2, container, false);       }      }   然后打开或新建activity_main.xml作为主Activity的布局文件,在里面加入两个Fragment的引用,使用android:name前缀来引用具体的Fragment: [html] view plaincopy <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:layout_width="match_parent"       android:layout_height="match_parent"       android:baselineAligned="false" >          <fragment           android:id="@+id/fragment1"           android:name="com.example.fragmentdemo.Fragment1"           android:layout_width="0dip"           android:layout_height="match_parent"           android:layout_weight="1" />          <fragment           android:id="@+id/fragment2"           android:name="com.example.fragmentdemo.Fragment2"           android:layout_width="0dip"           android:layout_height="match_parent"           android:layout_weight="1" />      </LinearLayout>   最后打开或新建MainActivity作为程序的主Activity,里面的代码非常简单,都是自动生成的: [java] view plaincopy public class MainActivity extends Activity {          @Override       protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.activity_main);       }      }   现在我们来运行一次程序,就会看到,一个Activity很融洽地包含了两个Fragment,这两个Fragment平分了整个屏幕,效果图如下: 动态添加Fragment 你已经学会了如何在XML中使用Fragment,但是这仅仅是Fragment最简单的功能而已。Fragment真正的强大之处在于可以动态地添加到Activity当中,因此这也是你必须要掌握的东西。当你学会了在程序运行时向Activity添加Fragment,程序的界面就可以定制的更加多样化。下面我们立刻来看看,如何动态添加Fragment。 还是在上一节代码的基础上修改,打开activity_main.xml,将其中对Fragment的引用都删除,只保留最外层的LinearLayout,并给它添加一个id,因为我们要动态添加Fragment,不用在XML里添加了,删除后代码如下: [html] view plaincopy <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:id="@+id/main_layout"       android:layout_width="match_parent"       android:layout_height="match_parent"       android:baselineAligned="false" >      </LinearLayout>   然后打开MainActivity,修改其中的代码如下所示: [java] view plaincopy public class MainActivity extends Activity {          @Override       protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.activity_main);           Display display = getWindowManager().getDefaultDisplay();           if (display.getWidth() > display.getHeight()) {               Fragment1 fragment1 = new Fragment1();               getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit();           } else {               Fragment2 fragment2 = new Fragment2();               getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit();           }       }      }   首先,我们要获取屏幕的宽度和高度,然后进行判断,如果屏幕宽度大于高度就添加fragment1,如果高度大于宽度就添加fragment2。动态添加Fragment主要分为4步: 1.获取到FragmentManager,在Activity中可以直接通过getFragmentManager得到。 2.开启一个事务,通过调用beginTransaction方法开启。 3.向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例。 4.提交事务,调用commit方法提交。 现在运行一下程序,效果如下图所示: 如果你是在使用模拟器运行,按下ctrl + F11切换到竖屏模式。效果如下图所示:                                  Fragment的生命周期 和Activity一样,Fragment也有自己的生命周期,理解Fragment的生命周期非常重要,我们通过代码的方式来瞧一瞧Fragment的生命周期是什么样的: [java] view plaincopy public class Fragment1 extends Fragment {       public static final String TAG = "Fragment1";          @Override       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {           Log.d(TAG, "onCreateView");           return inflater.inflate(R.layout.fragment1, container, false);       }          @Override       public void onAttach(Activity activity) {           super.onAttach(activity);           Log.d(TAG, "onAttach");       }          @Override       public void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           Log.d(TAG, "onCreate");       }          @Override       public void onActivityCreated(Bundle savedInstanceState) {           super.onActivityCreated(savedInstanceState);           Log.d(TAG, "onActivityCreated");       }          @Override       public void onStart() {           super.onStart();           Log.d(TAG, "onStart");       }          @Override       public void onResume() {           super.onResume();           Log.d(TAG, "onResume");       }          @Override       public void onPause() {           super.onPause();           Log.d(TAG, "onPause");       }          @Override       public void onStop() {           super.onStop();           Log.d(TAG, "onStop");       }          @Override       public void onDestroyView() {           super.onDestroyView();           Log.d(TAG, "onDestroyView");       }          @Override       public void onDestroy() {           super.onDestroy();           Log.d(TAG, "onDestroy");       }          @Override       public void onDetach() {           super.onDetach();           Log.d(TAG, "onDetach");       }      }   可以看到,上面的代码在每个生命周期的方法里都打印了日志,然后我们来运行一下程序,可以看到打印日志如下: 这时点击一下home键,打印日志如下: 如果你再重新进入进入程序,打印日志如下: 然后点击back键退出程序,打印日志如下: 看到这里,我相信大多数朋友已经非常明白了,因为这和Activity的生命周期太相似了。只是有几个Activity中没有的新方法,这里需要重点介绍一下: onAttach方法:Fragment和Activity建立关联的时候调用。onCreateView方法:为Fragment加载布局时调用。onActivityCreated方法:当Activity中的onCreate方法执行完后调用。onDestroyView方法:Fragment中的布局被移除时调用。onDetach方法:Fragment和Activity解除关联的时候调用。 Fragment之间进行通信 通常情况下,Activity都会包含多个Fragment,这时多个Fragment之间如何进行通信就是个非常重要的问题了。我们通过一个例子来看一下,如何在一个Fragment中去访问另一个Fragment的视图。 还是在第一节代码的基础上修改,首先打开fragment2.xml,在这个布局里面添加一个按钮: [html] view plaincopy <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:layout_width="match_parent"       android:layout_height="match_parent"       android:orientation="vertical"       android:background="#ffff00" >          <TextView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:text="This is fragment 2"           android:textColor="#000000"           android:textSize="25sp" />              <Button            android:id="@+id/button"           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:text="Get fragment1 text"           />      </LinearLayout>   然后打开fragment1.xml,为TextView添加一个id: [html] view plaincopy <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:layout_width="match_parent"       android:layout_height="match_parent"       android:background="#00ff00" >          <TextView           android:id="@+id/fragment1_text"           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:text="This is fragment 1"           android:textColor="#000000"           android:textSize="25sp" />      </LinearLayout>   接着打开Fragment2.java,添加onActivityCreated方法,并处理按钮的点击事件: [java] view plaincopy public class Fragment2 extends Fragment {          @Override       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {           return inflater.inflate(R.layout.fragment2, container, false);       }          @Override       public void onActivityCreated(Bundle savedInstanceState) {           super.onActivityCreated(savedInstanceState);           Button button = (Button) getActivity().findViewById(R.id.button);           button.setOnClickListener(new OnClickListener() {               @Override               public void onClick(View v) {                   TextView textView = (TextView) getActivity().findViewById(R.id.fragment1_text);                   Toast.makeText(getActivity(), textView.getText(), Toast.LENGTH_LONG).show();               }           });       }      }   现在运行一下程序,并点击一下fragment2上的按钮,效果如下图所示: 我们可以看到,在fragment2中成功获取到了fragment1中的视图,并弹出Toast。这是怎么实现的呢?主要都是通过getActivity这个方法实现的。getActivity方法可以让Fragment获取到关联的Activity,然后再调用Activity的findViewById方法,就可以获取到和这个Activity关联的其它Fragment的视图了。 好了,以上就是关于Fragment你所须知道的一切。 =================================== ==================================== 记得我之前参与开发过一个华为的项目,要求程序可以支持好几种终端设备,其中就包括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,里面有如下自动生成的内容: [java] view plaincopy public class MainActivity extends Activity {          @Override       public void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.activity_main);       }      }   作为一个Android老手,上面的代码实在太小儿科了,每个Activity中都会有这样的代码。不过今天我们的程序可不会这么简单,加载布局这一块还是大有文章的。 打开或新建res/layout/activity_main.xml作为程序的主布局文件,里面代码如下: [html] view plaincopy <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       xmlns:tools="http://schemas.android.com/tools"       android:layout_width="fill_parent"       android:layout_height="fill_parent"       android:orientation="horizontal"       tools:context=".MainActivity" >          <fragment           android:id="@+id/menu_fragment"           android:name="com.example.fragmentdemo.MenuFragment"           android:layout_width="fill_parent"           android:layout_height="fill_parent"           />      </LinearLayout>   这个布局引用了一个MenuFragment,我们稍后来进行实现,先来看一下今天的一个重点,我们需要再新建一个activity_main.xml,这个布局文件名和前面的主布局文件名是一样的,但是要放在不同的目录下面。 在res目录下新建layout-large目录,然后这个目录下创建新的activity_main.xml,加入如下代码: [html] view plaincopy <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       xmlns:tools="http://schemas.android.com/tools"       android:layout_width="fill_parent"       android:layout_height="fill_parent"       android:orientation="horizontal"       android:baselineAligned="false"       tools:context=".MainActivity"       >          <fragment           android:id="@+id/left_fragment"           android:name="com.example.fragmentdemo.MenuFragment"           android:layout_width="0dip"           android:layout_height="fill_parent"           android:layout_weight="1"           />              <FrameLayout            android:id="@+id/details_layout"           android:layout_width="0dip"           android:layout_height="fill_parent"           android:layout_weight="3"           ></FrameLayout>      </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,具体代码如下: [java] view plaincopy public class MenuFragment extends Fragment implements OnItemClickListener {          /**       * 菜单界面中只包含了一个ListView。       */       private ListView menuList;          /**       * ListView的适配器。       */       private ArrayAdapter<String> adapter;          /**       * 用于填充ListView的数据,这里就简单只用了两条数据。       */       private String[] menuItems = { "Sound", "Display" };          /**       * 是否是双页模式。如果一个Activity中包含了两个Fragment,就是双页模式。       */       private boolean isTwoPane;          /**       * 当Activity和Fragment建立关联时,初始化适配器中的数据。       */       @Override       public void onAttach(Activity activity) {           super.onAttach(activity);           adapter = new ArrayAdapter<String>(activity, android.R.layout.simple_list_item_1, menuItems);       }          /**       * 加载menu_fragment布局文件,为ListView绑定了适配器,并设置了监听事件。       */       @Override       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {           View view = inflater.inflate(R.layout.menu_fragment, container, false);           menuList = (ListView) view.findViewById(R.id.menu_list);           menuList.setAdapter(adapter);           menuList.setOnItemClickListener(this);           return view;       }          /**       * 当Activity创建完毕后,尝试获取一下布局文件中是否有details_layout这个元素,如果有说明当前       * 是双页模式,如果没有说明当前是单页模式。       */       @Override       public void onActivityCreated(Bundle savedInstanceState) {           super.onActivityCreated(savedInstanceState);           if (getActivity().findViewById(R.id.details_layout) != null) {               isTwoPane = true;           } else {               isTwoPane = false;           }       }          /**       * 处理ListView的点击事件,会根据当前是否是双页模式进行判断。如果是双页模式,则会动态添加Fragment。       * 如果不是双页模式,则会打开新的Activity。       */       @Override       public void onItemClick(AdapterView<?> arg0, View view, int index, long arg3) {           if (isTwoPane) {               Fragment fragment = null;               if (index == 0) {                   fragment = new SoundFragment();               } else if (index == 1) {                   fragment = new DisplayFragment();               }               getFragmentManager().beginTransaction().replace(R.id.details_layout, fragment).commit();           } else {               Intent intent = null;               if (index == 0) {                   intent = new Intent(getActivity(), SoundActivity.class);               } else if (index == 1) {                   intent = new Intent(getActivity(), DisplayActivity.class);               }               startActivity(intent);           }       }      }   这个类的代码并不长,我简单的说明一下。在onCreateView方法中加载了menu_fragment这个布局,这个布局里面包含了一个ListView,然后我们对这个ListView填充了两个简单的数据 "Sound" 和 "Display" 。又在onActivityCreated方法中做了一个判断,如果Activity的布局中包含了details_layout这个元素,那么当前就是双页模式,否则就是单页模式。onItemClick方法则处理了ListView的点击事件,发现如果当前是双页模式,就动态往details_layout中添加Fragment,如果当前是单页模式,就直接打开新的Activity。 我们把MenuFragment中引用到的其它内容一个个添加进来。新建menu_fragment.xml文件,加入如下代码: [html] view plaincopy <?xml version="1.0" encoding="UTF-8"?>   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:layout_width="fill_parent"       android:layout_height="fill_parent" >              <ListView           android:id="@+id/menu_list"           android:layout_width="fill_parent"           android:layout_height="fill_parent"           ></ListView>      </LinearLayout>   然后新建SoundFragment,里面内容非常简单: [java] view plaincopy public class SoundFragment extends Fragment {          @Override       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {           View view = inflater.inflate(R.layout.sound_fragment, container, false);           return view;       }      }   这里SoundFragment需要用到sound_fragment.xml布局文件,因此这里我们新建这个布局文件,并加入如下代码: [html] view plaincopy <?xml version="1.0" encoding="utf-8"?>   <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:layout_width="match_parent"       android:layout_height="match_parent"       android:background="#00ff00"       android:orientation="vertical" >              <TextView            android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:layout_centerInParent="true"           android:textSize="28sp"           android:textColor="#000000"           android:text="This is sound view"           />      </RelativeLayout>   同样的道理,我们再新建DisplayFragment和display_fragment.xml布局文件: [java] view plaincopy public class DisplayFragment extends Fragment {          public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {           View view = inflater.inflate(R.layout.display_fragment, container, false);           return view;       }   }   [html] view plaincopy <?xml version="1.0" encoding="utf-8"?>   <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"       android:layout_width="match_parent"       android:layout_height="match_parent"       android:background="#0000ff"       android:orientation="vertical" >              <TextView            android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:layout_centerInParent="true"           android:textSize="28sp"           android:textColor="#000000"           android:text="This is display view"           />      </RelativeLayout>   然后新建SoundActivity,代码如下: [java] view plaincopy public class SoundActivity extends Activity {          @Override       protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.sound_activity);       }      }   这个Activity只是加载了一个布局文件,现在我们来实现sound_activity.xml这个布局文件: [html] view plaincopy <?xml version="1.0" encoding="utf-8"?>   <fragment xmlns:android="http://schemas.android.com/apk/res/android"       android:id="@+id/sound_fragment"       android:name="com.example.fragmentdemo.SoundFragment"       android:layout_width="match_parent"       android:layout_height="match_parent" >      </fragment>   这个布局文件引用了SoundFragment,这样写的好处就是,以后我们只需要在SoundFragment中修改代码,SoundActivity就会跟着自动改变了,因为它所有的代码都是从SoundFragment中引用过来的。 好,同样的方法,我们再完成DisplayActivity: [java] view plaincopy public class DisplayActivity extends Activity {          @Override       protected void onCreate(Bundle savedInstanceState) {           super.onCreate(savedInstanceState);           setContentView(R.layout.display_activity);       }      }   然后加入display_activity.xml: [html] view plaincopy <?xml version="1.0" encoding="utf-8"?>   <fragment xmlns:android="http://schemas.android.com/apk/res/android"       android:id="@+id/display_fragment"       android:name="com.example.fragmentdemo.DisplayFragment"       android:layout_width="match_parent"       android:layout_height="match_parent" >      </fragment>   现在所有的代码就都已经完成了,我们来看一下效果吧。 首先将程序运行在手机上,效果图如下: 分别点击Sound和Display,界面会跳转到声音和显示界面:             然后将程序在平板上运行,点击Sound,效果图如下: 然后点击Display切换到显示界面,效果图如下: 这样我们就成功地让程序同时兼容手机和平板了。当然,这只是一个简单的demo,更多复杂的内容需要大家自己去实现了。 源码下载: 源码下载

上一篇:http基础
下一篇:JSF运行流程和生命周期介绍

相关文章

相关评论