Android Fragment详解

来源:互联网 发布:旅游软件排行 编辑:程序博客网 时间:2024/05/16 00:44

一.  Fragment介绍

       Fragment是Android honeycomb 3.0新增的概念,Fragment名为碎片不过却和Activity十分相似,下面介绍下Android Fragment的作用和用法。Fragment用来描述一些行为或一部分用户界面在一个Activity中,你可以合并多个fragment在一个单独的activity中建立多个UI面板,同时重用fragment在多个activity中.你可以认为fragment作为一个activity中的一节模块 ,fragment有自己的生命周期,接收自己的输入事件,你可以添加或移除从运行中的activity.

       一个fragment必须总是嵌入在一个activity中,同时fragment的生命周期受activity而影响,举个例子吧,当activity 暂停,那么所有在这个activity的fragments将被destroy释放。然而当一个activity在运行比如resume时,你可以单独的操控每个fragment,比如添加或删除。

       Fragment作为Android 3.0的新特性,有些功能还是比较强大的,比如 合并两个Activity,如图

 

                                             

       如上图所示,用Activity A 表示文章标题列表,ActivityB表示文章具体内容。我们可以看到两个Activity通过两个Fragment合并到一个Activity的布局方式,对于平板等大屏幕设备来说有着不错的展示面板。不过因为Fragment和Activity的生命周期都比较复杂,下图表示的fragments的生命周期:

                                             

 

Activity、Fragment分别对比下:

                                                      

两个的生命周期很类似,也息息相关。

 

 

创建一个fragment你必须创建一个Fragment的子类或存在的子类,比如类似下面的代码

public static class AndroidFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) { 
               return inflater.inflate(R.layout.android_fragment, container, false);
    }
}

 

onCreate()
当fragment创建时被调用,你应该初始化一些实用的组件,比如在fragment暂停或停止时需要恢复的

onCreateView()
当系统调用fragment在首次绘制用户界面时,如果画一个UI在你的fragment你必须返回一个View当然了你可以返回null代表这个fragment没有UI.

那么如何添加一个Fragment到Activity中呢? Activity的布局可以这样写

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment 
android:name="com.android.cwj.ArticleListFragment"
            android:id="@+id/list"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
    <fragment 
android:name="com.android.cwj.ArticleReaderFragment"
            android:id="@+id/viewer"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
</LinearLayout>

最后提醒大家Fragment存在于Activity的ViewGroup中,按照继承关系大家就可以了解他的结构。

 

二、 Android Fragment使用

     通常地 fragment做为宿主activity UI的一部分, 被作为activity整个view hierarchy的一部分被嵌入. 有2种方法你可以添加一个fragment到activity layout:


一、在activity的layout文件中声明fragment

      你可以像为View一样, 为fragment指定layout属性(sdk3.0以后).
      例子是一个有2个fragment的activity:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
     <fragment android:name="com.example.news.ArticleListFragment"
            android:id="@+id/list"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
     <fragment android:name="com.example.news.ArticleReaderFragment"
            android:id="@+id/viewer"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
  </LinearLayout>

<fragment> 中的 android:name 属性指定了在layout中实例化的Fragment类. 

       当系统创建这个activity layout时, 它实例化每一个在layout中指定的fragment,并调用每一个上的onCreateView()方法,来获取每一个fragment的layout. 系统将从fragment返回的 View 直接插入到<fragment>元素所在的地方. 

注意: 每一个fragment都需要一个唯一的标识, 如果activity重启,系统可以用来恢复fragment(并且你也可以用来捕获fragment来处理事务,例如移除它.) 

有3种方法来为一个fragment提供一个标识:
  • 为 android:id 属性提供一个唯一ID.
  • 为 android:tag 属性提供一个唯一字符串.
  • 如果以上2个你都没有提供, 系统使用容器view的ID.

二、使用FragmentManager将fragment添加到一个已存在的ViewGroup.


       当activity运行的任何时候, 都可以将fragment添加到activity layout.只需简单的指定一个需要放置fragment的ViewGroup.为了在你的activity中操作fragment事务(例如添加,移除,或代替一个fragment),必须使用来自 FragmentTransaction 的API.

可以按如下方法,从你的Activity取得一个 FragmentTransaction 的实例:

FragmentManager fragmentManager = getFragmentManager(); 

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();


然后你可以使用 add() 方法添加一个fragment, 指定要添加的fragment, 和要插入的view.

ExampleFragment fragment = new ExampleFragment();

fragmentTransaction.add(R.id.fragment_container, fragment); 

fragmentTransaction.commit();

      add()的第一个参数是fragment要放入的ViewGroup, 由resource ID指定, 第二个参数是需要添加的fragment.一旦用FragmentTransaction做了改变,为了使改变生效,必须调用commit(). 

 

三、    FragmentManage FragmentTransaction介绍

FragmentManage:

FragmentManager能够实现管理activity中fragment. 通过调用activity的getFragmentManager()取得它的实例.

FragmentManager可以做如下一些事情:
1、使用findFragmentById() (用于在activity layout中提供一个UI的fragment)或findFragmentByTag()
   (适用于有或没有UI的fragment)获取activity中存在的fragment
2、将fragment从后台堆栈中弹出, 使用 popBackStack() (模拟用户按下BACK 命令).
3、使用addOnBackStackChangeListener()注册一个监听后台堆栈变化的listener.
 

FragmentTransaction:

      FragmentTransaction对fragment进行添加,移除,替换,以及执行其他动作。
从 FragmentManager 获得一个FragmentTransaction的实例 :
FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每一个事务都是同时要执行的一套变化.可以在一个给定的事务中设置你想执行的所有变化,使用诸如 add(), remove(), 和 replace().然后, 要给activity应用事务, 必须调用 commit().

在调用commit()之前, 你可能想调用 addToBackStack(),将事务添加到一个fragment事务的back stack. 这个back stack由activity管理, 并允许用户通过按下 BACK 按键返回到前一个fragment状态.

举个例子, 这里是如何将一个fragment替换为另一个, 并在后台堆栈中保留之前的状态:
 // Create new fragment and transaction  Fragment newFragment = new ExampleFragment();  FragmentTransaction transaction = getFragmentManager().beginTransaction();  // Replace whatever is in the fragment_container view with this fragment,  // and add the transaction to the back stack  transaction.replace(R.id.fragment_container, newFragment);  transaction.addToBackStack(null);  // Commit the transaction  transaction.commit();

在这个例子中, newFragment 替换了当前layout容器中的由R.id.fragment_container标识的fragment.通过调用 addToBackStack(), replace事务被保存到back stack, 因此用户可以回退事务,并通过按下BACK按键带回前一个fragment.

如果添加多个变化到事务(例如add()或remove())并调用addToBackStack(), 然后在你调用commit()之前的所有应用的变化会被作为一个单个事务添加到后台堆栈, BACK按键会将它们一起回退.

添加变化到 FragmentTransaction的顺序不重要, 除以下例外:
  • 必须最后调用 commit().
  • 如果添加多个fragment到同一个容器, 那么添加的顺序决定了它们在view hierarchy中显示的顺序.
当执行一个移除fragment的事务时, 如果没有调用 addToBackStack(), 那么当事务提交后, 那个fragment会被销毁,并且用户不能导航回到它. 有鉴于此, 当移除一个fragment时,如果调用了 addToBackStack(), 那么fragment会被停止, 如果用户导航回来,它将会被恢复.

提示: 对于每一个fragment事务, 你可以应用一个事务动画, 通过在提交事务之前调用setTransition()实现.

调用 commit() 并不立即执行事务.恰恰相反, 它将事务安排排期, 一旦准备好, 就在activity的UI线程上运行(主线程).如果有必要, 无论如何, 你可以从你的UI线程调用 executePendingTransactions() 来立即执行由commit()提交的事务. 但这么做通常不必要, 除非事务是其他线程中的job的一个从属.

警告: 你只能在activity保存它的状态(当用户离开activity)之前使用commit()提交事务.

如果你试图在那个点之后提交, 会抛出一个异常.这是因为如果activity需要被恢复, 提交之后的状态可能会丢失.对于你觉得可以丢失提交的状况, 使用 commitAllowingStateLoss().

 

四、Android Fragment 实例

     Fragment是Android honeycomb 3.0新增的概念,Android——Fragment介绍、Android Fragment使用Android FragmentManage FragmentTransaction介绍中做了关于Fragment的详细介绍。这一片主要通过一个实例了解Fragment的使用。

       先看下实例效果图:

                                                   

效果图的左边是一个列表,右边是列表item的详情。

先看一下布局文件(layout):

 

[html] view plaincopyprint?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="horizontal" android:layout_width="match_parent"  
  4.     android:layout_height="match_parent">  
  5.     <fragment  
  6.         class="com.fragment.main.TitlesFragment"  
  7.         android:id="@+id/titles" android:layout_weight="1"  
  8.         android:layout_width="0px" android:layout_height="match_parent" />  
  9.     <FrameLayout android:id="@+id/details" android:layout_weight="1"  
  10.         android:layout_width="0px" android:layout_height="match_parent"  
  11.         android:background="?android:attr/detailsElementBackground" />  
  12. </LinearLayout>  

        布局文件中使用了fragment标签和FrameLayout标签。Android Fragment使用 中介绍了2中嵌入Fragment的方法,这个实例中都用到,从布局文件看到有了fragment标签,这是一种使用方法,FrameLayout标签将会成为第二种加载fragment的载体view。

        看一下程序实现(com.fragment.main.TitlesFragment):

[java] view plaincopyprint?
  1. public class TitlesFragment extends ListFragment {  
  2.   
  3.     int mCurCheckPosition = 0;  
  4.     int mShownCheckPosition = -1;  
  5.   
  6.     @Override  
  7.     public void onActivityCreated(Bundle savedInstanceState) {  
  8.         super.onActivityCreated(savedInstanceState);   
  9.                                                           
  10.         setListAdapter(new ArrayAdapter<String>(getActivity(),  
  11.                 android.R.layout.simple_list_item_activated_1,  
  12.                 Shakespeare.TITLES)); //使用静态数组填充列表  
  13.         if (savedInstanceState != null) {   
  14.             mCurCheckPosition = savedInstanceState.getInt("curChoice"0);  
  15.             mShownCheckPosition = savedInstanceState.getInt("shownChoice", -1);  
  16.         }   
  17.             getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);   
  18.             showDetails(mCurCheckPosition);  
  19.     }  
  20.   
  21.     @Override  
  22.     public void onSaveInstanceState(Bundle outState) {  
  23.         super.onSaveInstanceState(outState);  
  24.   
  25.         outState.putInt("curChoice", mCurCheckPosition);  
  26.         outState.putInt("shownChoice", mShownCheckPosition);  
  27.     }  
  28.   
  29.     @Override  
  30.     public void onListItemClick(ListView l, View v, int position, long id) {  
  31.         showDetails(position);  
  32.     }  
  33.   
  34.     /** 
  35.      *显示listview item 详情 
  36.      */  
  37.     void showDetails(int index) {  
  38.         mCurCheckPosition = index;  
  39.             getListView().setItemChecked(index, true);  
  40.   
  41.             if (mShownCheckPosition != mCurCheckPosition) {  
  42.   
  43.                 DetailsFragment df = DetailsFragment.newInstance(index);  
  44.                 FragmentTransaction ft = getFragmentManager()  
  45.                         .beginTransaction();  
  46.                 ft.replace(R.id.details, df);  
  47.                 ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);  
  48.                 ft.commit();  
  49.                 mShownCheckPosition = index;  
  50.             }     
  51.     }  
  52.   
  53. }  


TitlesFragment

TitlesFragment继承自Fragment的子类ListFragment,使用了一个静态数组填充列表,重写了onListItemClick方法,showDetails方法展示ListView item的详情。

DetailsFragment df = DetailsFragment.newInstance(index);//获取详情Fragment的实例

FragmentTransaction ft = getFragmentManager().beginTransaction();//获取FragmentTransaction 实例

ft.replace(R.id.details, df);  //使用DetailsFragment 的实例

ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);

ft.commit();//提交

这里就使用到了Android Fragment使用中介绍的第二种加载fragment的方法。看一下DetailsFragment :

 

[java] view plaincopyprint?
  1. public class DetailsFragment extends Fragment {  
  2.   
  3.     /**     * Create a new instance of DetailsFragment, initialized to     * show the text at 'index'.     */  
  4.     public static DetailsFragment newInstance(int index) {  
  5.         DetailsFragment f = new DetailsFragment();  
  6.         // Supply index input as an argument.          
  7.         Bundle args = new Bundle();  
  8.         args.putInt("index", index);  
  9.         f.setArguments(args);  
  10.         return f;  
  11.     }  
  12.   
  13.     @Override  
  14.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  15.             Bundle savedInstanceState) {  
  16.         if (container == null) {              
  17.             return null;  
  18.         }  
  19.         ScrollView scroller = new ScrollView(getActivity());  
  20.         TextView text = new TextView(getActivity());  
  21.   
  22.         int padding = (int) TypedValue.applyDimension(  
  23.                 TypedValue.COMPLEX_UNIT_DIP, 4, getActivity().getResources()  
  24.                         .getDisplayMetrics());  
  25.         text.setPadding(padding, padding, padding, padding);  
  26.         scroller.addView(text);  
  27.         text.setText(Shakespeare.DIALOGUE[getArguments().getInt("index"0)]);  
  28.         return scroller;  
  29.     }  
  30. }  


 

DetailsFragment 中使用newInstance(int index)方法产生DetailsFragment 实例并接受整型参数,重载了onCreateView方法创建view。

 

0 0
原创粉丝点击