Android学习笔记十三.深入理解fragment(一)

深入理解fragment(一)
    Fragment是Android3.0引入的新API,可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的生命周期,单独处理自己的输入,在Activity运行的时候可以加载或者移除Fragment模块。 可以把Fragment设计成可以在多个Activity中复用的模块,当开发的应用程序同时适用于平板电脑和手机时,可以利用Fragment实现灵活的布局,改善用户体验。
技术分享
一、Fragment的特征
1.Fragment总是作为Activity界面的组成部分。Fragment可调用getActvity()方法获取它所在的Activity,Activity可调用FragmentManager的findFragmentById()或findFragmentByTag()方法来获取Fragment。其中,fragment的id或tag在其布局文件<fragment../>元素中定义。
2.在Activity运行过程中,可调用FragmentManager的add()、remove()、replace()、方法动态地添加、删除或替换Fragment。
3.一个Activity可以同时组合多个Fragment;反过来,一个Fragment也可能被多个Activity复用;
4.Fragment可以响应自己的输入事件、并拥有自己的声明周期,但它们的生命周期直接被其他所属的Activity的生命周期控制。
二、Fragment的生命周期
    因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activity是密切相关的。如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果Activity是stopped状态,这个Activity中所有的Fragment都不能被启动;如果Activity被销毁,那么它其中的所有Fragment都会被销毁。但是,当Activity在活动状态,可以独立控制Fragment的状态,比如加上或者移除Fragment。当这样进行fragment transaction(转换)的时候,可以把fragment放入Activity的back stack中,这样用户就可以进行返回操作。
1.Fragment的几个状态
(1)活动状态:当前Fragment位于前台,用户可见、可以获得焦点;
(2)暂停状态:其他Acitvity位于前台,该Fragment依然可见,只是不能获得焦点;
(3)停止状态:该Fragment不可见,失去焦点
(4)销毁状态:该fragment被完全删除,或者Fragment所在的Activity被结束
2.与Fragment有关的方法
    如最常见的就是onCreateView()方法,其作用是返回一个View对象将Fragment显示出来。
技术分享
三、Fragment所需的库
    使用Fragment时,需要继承Fragment或者Fragment的子类(DialogFragment, ListFragment, PreferenceFragment, WebViewFragment),所以Fragment的代码看起来和Activity的类似。由于Fragment是Android 3.0及以上系统特有的API,如果我们需要开发低版本的应用软件,则需要引入Fragment所支持的库。
    Support Library是一个提供了API库函数的JAR文件,这样就可以在旧版本的Android上使用一些新版本的APIs.比如android-support-v4.jar.它的完整路径是:
                <sdk>/extras/android/support/v4/android-support-v4.jar它就提供了Fragment的APIs,使得在Android 1.6 (API level 4)以上的系统都可以使用Fragment。使用该支持库需要分两步:
1.添加支持库到工程项目中
    将上述的包拷入libs项目下的libs文件夹,然后在项目的Properties中添加:右键单击项目,选Properties,左边选Java Build Path,然后Add External JARs…,添加android-support-v4.jar.
技术分享
技术分享
2.导入Fragment、FragmentManager到工程中
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
注意,当创建包含Fragment的Activity时,如果用的是Support Library,那么继承的就应该是FragmentActivity而不是Activity.
四、Fragment核心剖析
1.创建Fragement,并设置该Fragment的显示界面
    与创建Activity类似,开发者实现的Fragment必须继承Fragment基类,Android提供了4种Fragment:(1)DialogFragment-对话框界面的Fragment;(2)ListFragment-实现列表界面的Fragment;(3)PreferenceFragment-选项设置界面的Fragment;(4)WebViewFragment-WebView界面的Fragment。创建Fragment通常需要实现三个回调方法:
(1)onCreate()
    系统在创建Fragment的时候调用这个方法,这里应该初始化相关的组件,一些即便是被暂停或者被停止时依然需要保留的东西。 
(2)onCreateView() 
   当第一次绘制Fragment的UI时系统调用这个方法,必须返回一个View。组件View对应的布局文件,即为该Fragment将会显示的界面。如果Fragment不提供UI也可以返回null。 
注意,如果继承自ListFragment,onCreateView()默认的实现会返回一个ListView,所以不用自己实现。 
(3)onPause() 
   当用户离开Fragment时第一个调用这个方法,需要提交一些变化,因为用户很可能不再返回来。
示例1:实现一个Fragment的布局界面。通过onCreateView()方法,将example_fragment布局资源文件设置为该Fragment的显示界面。
源码:
public static class ExampleFragment extends Fragment
{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
  Bundle savedInstanceState)
    {
        // Inflate the layout for this fragment
         return inflater.inflate(R.layout.example_fragment,     //指明了当前的Fragment对应的资源文件
                            container,                 //父容器控件
                             false);                     //*表明是否链接该布局和其父容器。这里设置为false,是因为系统已经插入了                 
                                                             这个布局到父控件,设置为true将会产生多余的一个View Group*/ 
     }
}
注释:onCreateView方法中container参数代表该Fragment在Acitivity的父控件;savedInstanceState提供了上一个实例的数据。

2.把一个Fragment加入到Activity中
    Fragment不能单独存在,必须依附在Activity上。当Fragment被加入到指定的Activity中时,它会处在对应的View Group中。通常,Fragment加载到一个Activity有两种方式:一种是在layout中使用标签<fragment>声明;另一种方法是在代码中把Fragment加入到一个Activity的指定ViewGroup中
(1)通过被加载Activity的布局文件将Fragment加载到指定Acitivity
    我们只需在指定Activity的布局文件中,添加一个<fragment.../>元素将Fragment作为一个子标签加入即可。
示例2:实现加载一个列表Fragment到指定的Activity中显示
源码:
<?xml version="1.0" encoding="utf-8"?> 
<!-- 定义一个水平排列的LinearLayout,并指定使用中等分隔条 --> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="match_parent"	
android:layout_height="match_parent" 
android:orientation="horizontal" 
android:layout_marginLeft="16dp" 
android:layout_marginRight="16dp" 
android:divider="?android:attr/dividerHorizontal" 
android:showDividers="middle"> 
<!-- 使用资源文件方式:添加一个fragment到Activity中 --> 
    <fragment 
            android:name="com.example.android_fragment_1.BookListFragment" 
            android:id="@+id/book_list" 
            android:layout_height="match_parent" 
            android:layout_width="0dp" 
            android:layout_weight="1"/> 
</LinearLayout>

注释:android:name属性为该需要加载的Fragment完整类名(包.类名);android:id属性为该Fragment的唯一标识符ID,我们可以通过ID实现对该Fragment进行添加、删除等操作。

(2)利用FragmentManager和FragmentTransaction将一个Fragment加载到指定Activity的ViewGroup中
    当Activity处于Running状态下的时候,可以使用这种方法在Activity的布局中动态地加入Fragment,只需要指定加入这个Fragment的父View Group即可。
    FragmentManager主要完成获取指定Fragment,将Fragment从后台弹出栈、为后台Fragment注册一个监听器。
    FragmentTransaction则完成Fragment的添加、删除、替换等事务,每个FragmentTransaction可以包含多个对Fragment修改,比如包含调用了多了add()、remove()和replace()操作,最后还调用commit()方法提交事务即可。
    首先、通过FragmentManager对象获得一个FragmentTransaction对象,借助FragmentTransaction对象实现Acitivity对Fragment执行添加、删除、替换等操作。
    FragmentManager fragmentManager=getFragmentManager();    //如用的是支持包,则为getSupportFragmentManager()
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();//打开事务
   其次,将实现的名为BookListFragment的Fragment添加到View Group中
    BookListFragment booklistfragment = new BookListFragment();        //创建一个图书列表Fragment
    fragmentTransaction.add(R.id.fragment_container, booklistfragment );  //添加图书列表Fragment到标识为fragment_container容器
    fragmentTransaction.commit();                                                             //提交事务改变生效-使View容器显示图书列表Fragment
注释1:如果想使应用程序允许用户按BACK返回键回到替换Fragment之前的状态(即上一个Fragment),我们可以将事务添加到back栈。
       fragmentTransaction.addtoBackStack(null);
注释2:除了上述添加一个Fragment到Activity的View父容器中,还可以替换或者删除容器中已有的Fragment.调用commit()方法使得FragmentTransaction实例的改变生效。
        fragmentTransaction.replace(R.id.fragment_container, booklistfragment);    //替换容器类已有的Fragment
注释3R.id.fragment_container为当前Activity布局中ID为fragment_container的容器,在Activity界面布局文件中定义。
res/layout/main.xml
技术分享技术分享

3.Fragment与Activity通信
    通过第二步将一个Fragment加载到指定的Activity之后,该Fragment必须与Activity交互信息,这就需要Fragment能获取它所在的Activity,Activity也能获取它所包含的任意的Fragment。
1)获取目标对象
(1)Fragment获取它所在的Activity
    调用Fragment的getActivity()方法即可返回它所在的Activity。
(2)Activity获取它所包含的Fragment
    调用Activity关联的FragmentManager的findFragmentById(int id)或findFragmentByTag(String tag)方法即可获取指定的Fragment。
        ExampleFragment fragment =(ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment)
    注释:我们可以在Activity界面元件中使用<fragment.../>添加Fragment时,可以为<fragment../>元素添加android:name或android:tag属性来标识Fragment,接下来Activity将可以通过findFragmentById(int id)等方法来获取Fragment。

2)Frament与Activity之间的数据传递
(1)Activity向Fragment传递数据
    在Activity中创建Bundle数据包,并调用Fragment的setArguments(Bundle bundle)方法即可将Bundle数据包传给Fragment;
(2)Fragment向Activity传递数据或Activity需在Fragment运行中进行实时通信
    在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口,这样Fragment即可调用该回调方法将数据传给Activity。

3)activity响应fragment的事件完全解析
     有时,你可能需要fragment与activity共享事件。一个好办法是fragment中定义一个回调接口,然后在activity中实现这个回调接口
    例如,还是那个新闻程序的例子,它有一个activity,activity中含有两个fragment。fragmentA显示新闻标题,fragmentB显示标题对应的内容。fragmentA必须在用户选择了某个标题时告诉activity,然后activity再告诉fragmentB,fragmentB就显示出对应的内容(为什么这么麻烦?直接fragmentA告诉fragmentB不就行了?也可以啊,但是你的fragment就减少了可重用的能力。现在我只需把我的事件告诉宿主,由宿主决定如何处置,这样是不是重用性更好呢?)。如下例,
(1)在fragmentA(BookListFragment)中定义OnBookSelectedListener回调接口
public class BookListFragment extends ListFragment
 {
           private OnBookSelectedListener whichbook; //接口对象
 public interface OnBookSelectedListener
         {
             public void onBookSelected(Integer id); //参数为Map集合的键
         }
        .....
}

(2)  然后在activity实现接口OnBookSelectedListener,在方法onBookSelected()中通知fragmentB(BookDetailFragment ),即Activity从FragmentA获取传入的ID,用来启动FragmentB
如下所示:  
public class SelectBookActivity extends Activity implements BookListFragment.OnBookSelectedListener
{
    ......
     public void onItemSelected(Integer id)
     {
          //a.创建Bundle,准备向Fragment传入参数
          Bundle bundle=new Bundle();
           bundle.putInt(BookDetailFragment.ITEM_ID, id); //装入值id到"item_id"键
          //b.创建BookDetailFragment对象,并项Fragment传入参数
          BookDetailFragment fragment=new BookDetailFragment();
          fragment.setArguments( bundle);
          //c.使用fragment替换book_detail_container容器当前显示的Fragment
          getFragmentManager().beginTransaction()
                                            .replace(R.id.book_detail_container, fragment)
                                            .commit();
          /*注释:这一句等价于....
           * FragmentManager Manager=getFragmentManager();
           * FragmentTransaction Transaction=Manager.beginTransaction();
           * Transaction.replace(R.id.book_detail_container, Manager);
           * Transaction.commit();
           * */
     }
}                                                                                                                          
(3)实现fragmentA(BookListFragment)的onAttach()方法,将fragmentA添加并显示到Activity中
    当fragmentA添加到activity中时,会调用fragmentA的方法onAttach(),这个方法中适合检查activity是否实现了OnBookSelectedListener接口,检查方法就是对传入的activity的实例进行类型转换如果activity没有实现那个接口,fragment抛出ClassCastException异常。如果成功了,mListener成员变量保存OnArticleSelectedListener的实例。于是fragmentA就可以调用mListener的方法来与activity共享事件。
public void onAttach(Activity activity)
 {
      super.onAttach(activity);
      //a.如果Activity中没有实现Callbacks接口,抛出异常
      if(!(activity instanceof Callbacks))
      {
           throw new IllegalStateException("异常:BookListFragment所在的Activity必须实现Callback接口!");
      }
      //把该Activity当成whichbook对象(缺少这一句话,导致出现NullPointerException错误)
       whichbook=( BookListFragment)activity;
 }

(4)fragmentA中实现onListItemClick()响应activity发出的事件(Fragment向Activity传递ID)
      如果fragmentA是一个ListFragment,每次选中列表的一项时,就会调用fragmentA的onListItemClick()方法,在这个方法中调用onBookSelected()来与activity共享事件onBookSelected()传入的参数id是列表的被选中的行ID,另一个fragment(B)( BookDetailFragment )用这个ID来从程序的ContentProvider中取得标题的内容。
@当用户单击某列表项时激发该回调方法
 public void onListItemClick(ListView l, View v, int position, long id) {
      super.onListItemClick(l, v, position, id);
       OnBookSelectedListener.onBookSelected(BookContent.ITEMS.get(position).id);//激发 OnBookSelectedListener接口的onBookSelected方法
 }
技术分享
技术分享

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。