Fragments

    翻译这个单词,应该叫碎片。一个碎片代表一个行为或者活动中用户界面的一部分。可以在一个活动中混合多个碎片,创建一个

多面板的UI,同时可以在多个活动中重用碎片。 可以把碎片想成一个活动的模块化的部分,他有自己的生命周期,接收它自己的

输入事件,可以在活动运行的时候添加和删除(像子活动,可以在不同的活动中使用)。

    一个碎片必须嵌入一个活动中,因此碎片的生命周期直接受宿主活动的生命周期影响。举例来说,如果活动暂停,里面所有的

碎片暂停,如果活动销毁,里面所有的碎片也一样。然而,当一个活动在运行时(resume state),我们可以单独操作每一个

碎片,比如添加或者移除他们。当执行碎片这样的事务的时候,也可以把他加入到活动管理的返回栈中,活动中每个返回栈的

入口都是一系列碎片事务发生的地方。返回栈允许用户反转一个碎片事务通过按返回键这是什么意思?)

    当添加一个碎片作为活动的一部分的时候,他在活动的视图层级的一个ViewGroup中,并且这个活动定义他自己的视图布局。

可以插入一个碎片到活动中通过在活动的布局文件中添加<fragment>,或者用程序添加到已存在的ViewGroup中。然而,一个活动

并不是必须要作为活动布局的一部分,也可以使用一个没有布局的碎片作为活动的不可见的工作者?。


    设计哲学

    安卓从3.0开始引入碎片,主要是为了在大屏幕上支持动态和灵活的用户界面设计,比如平板。因为平板的屏幕比手机大很多,

有足够的空间去混合和交互用户界面组件。碎片允许这样的设计,无需管理负责的视图层级的变化。通过把活动的布局分割成

碎片,你可以在活动运行期间修改外观,在返回栈中保存这些改变。

    举例来说,一个新闻应用可以在左边使用碎片展示一系列的文章,在右边使用另一个碎片展现一篇文章,两个碎片出现在

同一个活动中,互相挨着,并且每个活动都有自己的一系列生命周期回调方法,处理他们自己的用户输入事件。这样,代替

使用一个活动去选择文章,使用另一个活动去读文章,用户可以在同一个活动中选择读取文章。

    应该把每一个活动设计成模块化可重复使用的活动组件。也就说,因为每一个碎片定义自己的布局和自己的行为通过自己的

生命回调函数,我们可以在多个活动引用碎片。所以应该设置为复用,避免直接操作一个活动到另一个活动。

这是非常重要的,因为模块化的碎片允许我们改变碎片的组合来适应不同的屏幕尺寸。当设计你的程序支持平板和手机的时候,

可以在不同的布局配置复用碎片来在可用的屏幕空间完善用户体验。比如,在手机上,可能需要分离碎片来提供一个单面板

的UI当一个不在同一个活动中适应?


要创建一个Fragment,必须创建一个Fragment的子类(或者一个已经存在的它的子类)。Fragment类的代码和Activity很像。它包含和Activity类似的回调方法,比如onCreate(),onStart(),onPause(),onStop。实际上,如果转换一个存在的安卓程序用碎片,可能仅仅需要把活动的回调方法代码移到碎片的各自回调方法中。

通常只是实现以下三个方法:

OnCreate:系统调用他当创建Fragment的时候。在我们的实现中,应该初始化Fragment必要的组件,这些组件在碎片暂停、停止、运行时你希望保存的。

onCreateView:系统调用这个方法当碎片要第一次绘制它的用户界面的时候。为了给碎片绘制一个用户界面,这个方法必须返回一个碎片的布局文件的根View。如果碎片不提供一个用户界面,可以返回空。

onPause:系统调用改方法作为第一个暗示,用户正在离开这个碎片(虽然他并不总是以为着碎片正在被销毁)。这里是通常应该提交需要保存的改变在当前用户会话之外。(因为用户可能不会回来了)

大多数应用应该至少为每一个碎片实现上面这三个方法,但是还有一些其他的回调方法应该使用来处理碎片生命周期的不同状态。

除了基本的Fragmnet类之外,还有几个他的子类我们可能会继承。DialogFragment:(在Dialog模块进行了讨论,那里主要介绍了使用Fragment来处理Dialog,还需要在学习,动手做)展示一个悬浮的对话框,使用这个类创建对话框是在活动中使用对话框辅助方法的一个好的代替,因为可以可以把一个碎片对话框嵌入活动管理的碎片栈,允许用户返回一个dismissed Fragment(不太明白)。

ListFragment:展示适配器管理的一些项目,很listActivity很像。他提供了几个方法管理一个list view,比如onListItemClick回调方法来处理点击事件。

PreferenceFragment这个以后再讨论吧

Displays ahierarchy of Preference objects as alist, similar to PreferenceActivity. This isuseful when creating a "settings" activity for your application.

增加一个用户界面

一个碎片通常作为一个活动的用户界面的一部分,把自己的布局贡献给活动。

为了给碎片提供一个布局,必须实现onCreateView回调方法,系统回调这个方法当碎片是适合绘制布局。我们实现这个方法必须返回碎片布局的根View。

注意:如果碎片是ListFragment的子类,默认onCreateView会返回一个ListVIew,不需要实现这个方法。

为了从onCreateView返回布局,可以从资源定义的xml填充他,为了帮助我们这样做,onCreateView方法提供了一个LayoutInflater对象。

举例来说,下面是一个Fragment的子类从xml文件加载布局。

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroupcontainer,Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment,container, false);
    }
}

 

这个container参数传递给onCreateView方法的是一个父ViweGroup(从活动的布局),我们的活动要插入的。 savedInstanceState参数是一个Bundle提供关于上一个Fragment的数据(上一个是系统处理的吧),如果碎片需要被重新resumed的。

    Inflate方法接收三个参数,这个之前讨论过,第一个是,资源的id,指定一个xml文件,第二个,要填充的布局的父ViewGroup,传递container是非常重要的为了让系统设置布局参数给填充布局的父view,确定他要去那个父view。

The ViewGroup to be the parentof the inflated layout. Passing the container is important in order for thesystem to apply layout parameters to the root view of the inflated layout,specified by the parent view in which it's going.

第三个参数,表示是否布局应该和第二个参数关联(这个例子是false,因为系统已经把布局插入到父容器中,传递true会创建一个重复的viewGroup在final的布局中。不是很理解,系统是怎么插入的,我们什么时候要设置true

  • A boolean indicating whether the inflated layout should be attached to the ViewGroup (the second parameter) during inflation. (In this case, this is false because the system is already inserting the inflated layout into the container—passing true would create a redundant view group in the final layout.)

现在我们已经知道如何创建一个碎片提供一个布局,下面,需要添加这个碎片到活动中

添加碎片到活动中

  通常一个碎片贡献给宿主活动部分UI,被嵌入为活动全部的view 层级作为一部分。有两种方式可以把碎片添加到活动布局中:

第一种:直接在活动的布局文件中定义Fragment,把Fragment当成一个view。下面是例子:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns: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" />
    <fragmentandroid: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属性指定碎片类去实例化这个布局。(碎片中也有自己的布局,所以也要实现一个布局,这就是view的嵌套了)。当系统创建这个活动布局的时候,会实例化布局中的每一个碎片调用他们的onCreateView方法,接收每一个碎片的布局。系统直接在碎片的位置插入碎片返回的这个view,(碎片就是一个标记,然后用自己onCreateView的方法返回的view替换掉,所以这个方法不能少,然后onCreate是创建这个view用的,也不能少 ,不创建的话,没法用啊)。

注意:每一个碎片需要一个唯一的标识,系统用来恢复这个碎片如果活动重启(可以用来捕获碎片的执行事务,比如删除他??)有三种方式提供id

(1) 设置android:id属性

(2) 设置android:tag属性

(3) 如果上面都不提供,系统会使用包含他的容器的id

或者使用程序添加碎片到一个存在的ViewGroup中。

活动执行的任何时间,都可以添加一个碎片到活动的布局中。仅仅需要指定哪个ViewGroup去添加这个碎片。

为了在活动中处理碎片的事务(比如增删改或者替换),必须使用FragmentTransaction的API。可以在活动中这样获得一个FragmentTransaction的实例。

FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

 

然后可以添加一个碎片使用add方法,指定碎片要添加到哪个布局中,像下面:

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

第一个参数是碎片要放置的ViewGroup,第二个是需要添加的碎片,其实很简单的。一旦FragmentTransaction有改变,必须调动coomit方法让变化生效。

Adding a fragment without a UI

添加一个没有UI的碎片

上面的例子展示了如何添加一个提供UI的碎片到活动中;然而,也可以使用碎片为活动提供一个后台行为,不展示额外的UI。(这是什么意思,background behavior)。

为了添加一个没有UI的碎片,从活动中添加碎片使用add(Fragment,String)为这个碎片提供一个唯一的tag,而不是一个view id ,这添加了一个碎片,但是,因为没有和活动布局的一个view关联,他不能接收onCreateView的调用,所以不需要实现这个方法(没有UI,还实现干嘛,闲的)。

提供一个String标签给碎片不完全用于没有UI的碎片,也可以给用UI的碎片提供string标签,但是如果碎片没有UI,string标签是唯一的方法来标识他(id在布局文件中,没有布局就没有id,只能用tag了,合理),如果希望在活动的后面获得碎片,使用findFragmentByTag。

 

管理碎片

为了管理活动中的碎片,需要使用FragmentManager。得到他,在活动中调用getFragmentManager。在FragmentManager身上可以做的的事情包括:

(1)      得到活动中存在的碎片,使用findFragmentById或者findFragmentByTag

(2)      使用popBackStack把碎片推出返回栈(模拟用户按返回键,但是不仅仅这么简单吧)

(3)      为返回栈的变化注册一个监听器,使用addOnBackStackChangedListener。

也可以使用FragmentManager打开一个FragmentTransaction,进行碎片的事务管理。

碎片事务管理

在活动中使用碎片的最大的一个特点是,增加删除替换或者执行其他动作,响应用户的交互。(特点在哪里)。每个提交给活动的变化集叫做事务,可以使用FragmentTransaction提供的API展现一个事务。也可以保存每个事务到活动管理的返回栈中,允许用户通过碎片变化导航返回(和通过活动返回很像,不太理解)

A great feature about using fragments in your activity isthe ability to add, remove, replace, and perform other actions with them, inresponse to user interaction. Each set of changes that you commit to theactivity is called a transaction and you can perform one using APIs in FragmentTransaction.You can also save each transaction to a back stack managed by the activity,allowing the user to navigate backward through thefragment changes (similar to navigating backward through activities).

像下面从FragmentManager实例化一个FragmentTransaction,

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每个事务是一系列的变化我们想同时执行。也可以设置想执行的所有变化到一个事务中,使用add remove replace方法。然后想应用这个事务到活动中,必须调用commit方法。

在调用commit之前,可能想调用addToBackStack来添加这个事务到碎片事务的返回栈。这个返回栈有活动管理,允许用户返回上一个碎片的状态通过点击返回按钮。

下面展示了如果替换一个碎片,然后保存上一个的状态到返回栈中。

// 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();

在这个例子中,ExampleFragment代替了任何布局容器通过id识别的一个碎片,通过调用addToBackStack,代替的事务被保存到返回栈中,所以用户可以倒退这个事务,返回到之前的碎片通过点击返回键(如果不调用addToBackStack的话,会怎样)

如果添加了多个变化给事务,然后调用addToBackStack,在调用commit之前的所有变化都被添加到返回栈中作为一个单独的事务,返回键会把他们全部倒退(这就是回滚吧,我调用commit之前的所有变化作为一个事务,回滚,回到第一个地方吗,比如先add 后remove,回到add吗)

添加到FragmentTransaction变化不重要,除非:

(1) 你必须最后调用commit

(2) 如果添加多个碎片到同一个容器,调用的顺序决定他们显示的顺序

  • You must call commit() last
  • If you're adding multiple fragments to the same container, then the order in which you add them determines the order they appear in the view hierarchy

如果执行一个移除碎片的事务的时候不调用addToBackStack,这么碎片会被销毁当事务提交,用户不能返回到他(可以新建一个吧,我这个时候按返回不是新建吗??)然而,如果你调用addToBackStack当移除一个碎片,碎片停止了,如果用户返回会重新被resumed.

建议:对于每个碎片事务,可以设置一个事务动画通过调用setTransition在commit之前。

调用commit不马上执行这个事务。当然,他被列到UI进程中,一旦进程有能力这么做。如果需要,然后,你可以在UI线程中调用executePendingTransactions来立马执行commit提交的事务。这样一般是没有必要的,出发这个事务是其他线程的一个依赖。

注意:(不太理解,commit什么时候提交??)

Caution: You can commit a transaction using commit() only prior to the activity saving its state (when the user leaves the activity). If youattempt to commit after that point, an exception will be thrown. This isbecause the state after the commit can be lost if the activity needs to berestored. For situations in which its okay that you lose the commit, use commitAllowingStateLoss().

 

和活动通信

虽然一个碎片的实现作为一个对象,独立于活动,可以在多个活动中使用,一个给定的碎片实例直接绑定到包含他的活动上。

特别的,碎片可以通过getActivity方法获得活动的实例,然后很轻松的实现任务比如查找活动中的一个view:

View listView = getActivity().findViewById(R.id.list);
 
同理,活动可以调用碎片中的方法,通过从FragmentManager,使用findFragmentById()findFragmentByTag()获得一个碎片的引用

ExampleFragment fragment = (ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment);

 

为活动创建事件回调

看疯狂安卓,我的理解就是,在Fragment中定义一个接口,Activity实现这个接口,在Fragment注册监听器的时候,getActiviy转为这个接口,设置进去,就可以了。下面看看官方的:

在某些情况下,可能需要一个碎片和活动共享事件。一个好的方式是在碎片中定义一个回调接口,并且要求宿主活动实现它。当活动通过接口接收一个回调,可以在需要的时候和布局中的其他碎片分析信息。

举例来说,如果一个新闻应用有两个碎片在一个活动中,一个展示一系列的文章(碎片A),另一个(碎片B)展示文章详情,然后碎片A比如告诉活动当一个标题被选中,这样活动可以告诉碎片B去展示这个文章。在这个例子中,碎片A定义了一个OnArticleSelectedListener接口。

public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}

然后宿主活动实现OnArticleSelectedListener接口,重写onArticleSelected()方法通知碎片B来自碎片A的事件。为了确保宿主活动实现这个接口,碎片A的onAttach回调方法(系统调用当添加一个碎片到这个活动)实例化一个OnArticleSelectedListener接口通过强制转型传递给onAttach的活动:

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener)activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()+ " must implement OnArticleSelectedListener");
        }
    }
    ...
}

    如果活动没有实现这个接口,碎片会抛出ClassCastException异常。成功的话,mListener变量拥有一个引用指向活动对于OnArticleSelectedListener的实现,所以碎片A可以通过调用OnArticleSelectedListener接口定义的方法和活动分享事件。举例来说,如果碎片继承ListFragment,用户每次点击选项的时候,系统在碎片中调用onListItemClick,然后调用onArticleSelected方法,去和活动分享这个事件。(就是在onListItemClick显示调用onArticleSelected方法,显示的绑定啊,强制捆绑在一起,意义何在

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, longid) {
        // Append the clicked item's row ID with thecontent provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI,id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }
    ...
}

 

id参数传递给onListItemClick的是用户点击的行数 ,活动或其他碎片使用它来从其他应用的ContentProvider获得文章。

 

给Action Bar添加选项(在ActionBar里详细看吧)

    碎片可以为活动的OptionsMenu(and, consequently, the ActionBar,也就是说是Action Bar?)提供菜单项通过实现onCreateOptionsMenu。

Your fragments can contribute menu items to the activity's Options Menu (and,consequently, the Action Bar) byimplementing onCreateOptionsMenu(). In order for this method to receive calls, however, you must call setHasOptionsMenu() during onCreate(), to indicatethat the fragment would like to add items to the Options Menu (otherwise, thefragment will not receive a call to onCreateOptionsMenu()).

Any items that you then add to the Options Menu from thefragment are appended to the existing menu items. The fragment also receivescallbacks to onOptionsItemSelected()when a menu item is selected.

You can also register a view in your fragment layout toprovide a context menu by calling registerForContextMenu().When the user opens the context menu, the fragment receives a call to onCreateContextMenu().When the user selects an item, the fragment receives a call to onContextItemSelected().

 

生命周期:

 

 

碎片的生命周期比活动稍微多了几个状态,不过大体是一致的:

和活动类似,可以停留的三个状态:

Resumed

碎片在运行中的可见

Paused

     另一个活动在前面,并且拥有焦点,但是碎片所在的活动仍然可见(前面的活动部分通明,不覆盖整个屏幕  这个状态就是和活动的生命周期相关联了

Stopped

碎片不可见,要是宿主活动停止了,或者碎片被从活动中移除,但是添加到了返回栈(不然是不是直接销毁了)。一个停止的碎片仍然是活的(所有的状态和成员信息都被系统保存),然后他不在可见,并且如果活动被杀死了,他也会被杀死。

和活动类似,可以通过Bundle保持碎片的状态,以防活动进程被杀死,并且需要在活动重新创建的时候恢复碎片的状态。可以在碎片的onSaveInstanceState回调方法保持状态信息,在onCreate(), onCreateView(), or onActivityCreated()恢复。

碎片和活动的生命周期最大的不同就是他们在各自的返回栈如何保存。活动在停止的时候会被放进系统管理的一系列活动的返回栈,默认(用户可以navigate back,通过返回按钮,这个要去Tasksand Back Stack学习看看)。然而,一个碎片被放进宿主活动管理的返回栈仅仅当我们显示的调用addToBackStack在移除碎片的事务中。

另外,管理碎片的生命周期和管理活动的生命周期非常像。所以,管理活动的生命周期的例子也适用于碎片;还需要理解的是,活动的生命周期如何影响碎片的生命周期。

    小心:如果需要为碎片提供上下文对象,可以调用个推Activy方法,仅仅当碎片attached到活动中要小心。当碎片不在attached,或者检测到到了他生命的终点,getActivity返回null。

    Coordinating withthe activity lifecycle

    碎片所在的活动的生命周期直接影响碎片的生命周期,因此活动的每一次生命周期回调导致每一个碎片一次类似的回调。举例来说,当活动接收onPause,活动中每个碎片接收onPause。

   

 碎片有几个额外的生命周期回调方法,然而,这些处理和活动的独特的交互执行动作,比如建立销毁碎片的UI。这些额外的方法是:

onAttach()

    当碎片和活动关联的时候调用(活动别传入进去)

Called when thefragment has been associated with the activity (the Activity is passed in here).

onCreateView()

     当创建碎片的view层级的时候

Called to createthe view hierarchy associated with the fragment.

onActivityCreated()

     当活动的onCreate方法返回的时候调用

Called when theactivity's onCreate() method has returned.

onDestroyView()

      当碎片的view层级被销毁

Called when theview hierarchy associated with the fragment is being removed.

onDetach()

      当碎片和活动分离时调用

Called when thefragment is being disassociated from the activity.

 

碎片的生命周期流,被宿主活动影响,如上图展示。在这个图片中,可以看到活动的每一个成功的状态调用决定碎片的哪个回调方法可能被调用。举例,当活动接收onCreate,活动中的碎片接收到仅仅是onActivityCreated回调方法

一旦一个活动到达resumed状态,可以很随意的在活动增加移除碎片。一次,只用在活动在resumed状态,碎片的生命周期独立的改变(不受宿主活动的影响

然而,当活动离开resumed状态,碎片又一次被活动推到他的生命周期中。

However, when the activity leaves the resumed state, thefragment again is pushed through its lifecycle by the activity.

 

 





    


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值