频道栏目
首页 > 程序开发 > 移动开发 > Android > 正文
fragment-Android 6.0开发者文档
2018-04-14 09:41:51         来源:一朵翔云的博客  
收藏   我要投稿

fragment-Android 6.0开发者文档

fragment可以完成activity的一部分行为或UI。你可以在一个activity中添加多个fragment,以建立一个多模块的UI,另外,你可以在不同的activity中重用这些fragment。你可以把fragment理解为activity的模块化的一部分,它有自己的生命周期,接收自己的事件,在activity运行时可以动态添加或删除(有点像“子activity”,你可以在不同activity重用它)。

fragment必须植入到activity中,它的生命周期是被宿主activity的生命周期完全影响的。例如,当activity进入paused状态,fragment也会进入paused状态,当activity被销毁,fragment也会被销毁。然而,当activity在运行时(在resumed状态),你可以单独的操作某个fragment,例如添加或删除它们。当你对某个fragment进行操作时,你可以将这个操作添加到由activity管理的一个回退栈中(这个回退栈的每个节点都是某个fragment动作的记录)。这个回退栈可以让用户在按下back按钮后撤销fragment动作。

当你将fragment添加到activity的布局中时,它就处于activity视图层的一个ViewGroup中,这时fragment可以定义它自己的视图布局。你可以activity的布局文件中添加元素,或在代码中将fragment加入到某个已存在的ViewGroup中,来将fragment添加到activity中。但是,这并不是说fragment必须作为activity布局的一部分,你可以在activity中添加没有UI的fragment去做一些不需要界面的工作。

本文档描述了怎样将fragment运用到你的应用中,包括当fragment被加入到activity回退栈时怎样维持自己的状态、fragment怎样与activity和此activity的其他fragment共享事件等等。


设计理念

Android在Android 3.0(API 11)加入fragment功能,最初的目标是在大屏幕上实现更灵活的动态UI设计,如平板上。由于平板的屏幕比手机大很多,也就多出了很多能容纳UI组件的空间。fragment将你从管理复杂的视图层级变化中解救出来。通过将activity的布局分割为多个fragment,你可以在运行时修改activity的界面,并且将这些修改保存到activity管理的回退栈中。

例如,应用可以在界面左边使用一个fragment显示文章列表,在右边显示某个文章的详细信息————这两个fragment处于同一个activity中,一边一个,每个fragment都有自己的生命周期回调方法,并且能分别处理它们自己的输入事件。这样的话,用户就不需要在一个activity选择文章,而另一个activity显示文章了,用户可以在同一个activity中选择文章然后阅读它,就像图1中表现的那样。
图1

你应该将fragment设计为一个模块化、可重复利用的activity组件。每个fragment都有自己的布局和生命周期回调,你可以在不同的activity中使用这个fragment,因此你应该将fragment设计为可重用的,另外你应该避免在fragment中直接操作另一个fragment。重用性对于fragment很重要,因为你需要用fragment的组合去适应不同的屏幕大小。当你将你的应用设计为既支持平板又支持手机时,你可以在不同的布局配置中重用这些fragment,从而让用户可以在不同的屏幕大小中获得最优的体验。一个典型例子是:在手机上,有必要将平板上一个activity可以放下的多个fragment分解为多个activity。

在图1中,当应用运行在平板上时,可以在activity A中放入两个fragment,而在手机上,没有足够的空间能放下两个fragment,所以activity A只包含文章列表的fragment,当用户选择某个文章时,启动activity B,其包含另一个显示文章信息的fragment。这样的话,应用就通过重用fragment同时支持了平板和手机。


创建fragment

图2

你可以通过继承Fragment或Fragment的子类来创建一个fragment。Fragment类的代码看起来很像Activity类。它的回调方法跟activity的回调方法很像,如onCreate()、onStart()、onPause()、onStop()。事实上,如果你的应用想要针对fragment做重构,你会发现activity的回调方法可以很简单的移植到各个fragment中。

通常,对于fragment你需要至少实现以下生命周期方法:

onCreate()
当创建fragment时系统会调用此方法。在此方法中,你应该做一些必要的初始化工作。

onCreateView()
当fragment第一次绘制用户界面时系统会调用此方法。如果你的fragment要绘制UI,你必须在这个方法返回一个表示fragment布局的View,当然,如果你的fragment不需要UI,你可以返回null。

onPause()
当用户离开此fragment时系统会调用此方法。在这个方法中,你应该保存当前用户会话的所有数据,因为用户可能不会回到此fragment了。

应用至少需要为每个fragment实现以上三个方法,当然,你也可以实现其他几个方法来处理fragment的其他生命周期。其他方法的信息请看“处理Fragment生命周期”部分。

除了Fragment基础类,你也可以继承其他几个Fragment的子类:

DialogFragment
表示一个浮动的对话框。使用这个类创建对话框的好处是可以将此fragment加入到activity管理的fragment回退栈中,从而使用户在fragment关闭的情况下可以重新返回此fragment。

ListFragment
显示一个item的列表(列表数据由adapter管理,如SimpleCursorAdapter),类似ListActivity。它提供了几个管理列表view的方法,例如onListItemClick()方法,可以处理点击事件。

PreferenceFragment
显示一个Preference对象的列表,类似PreferenceActivity。它在创建设置界面时很有用。

添加用户界面

fragment通常有自己的布局,并作为activity界面的一部分。

要为fragment添加布局,你必须实现onCreateView()方法,当fragment需要绘制布局时系统会调用它。在这个方法中,你必须返回一个表示此fragment布局的View。

注意:如果你的fragment继承了ListFragment,由于ListFragment的默认实现会在onCreateView()方法中返回一个ListView,所以你可以不实现onCreateView()方法。

你可以用XML定义的layout资源填充一个View作为onCreateView()的返回。系统提供LayoutInflater对象帮助你填充这个View。

使用方法如下(下例中Fragment从example_fragment.xml加载布局):

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, container, false);
    }
}

onCreateView()的container参数是一个ViewGroup,表示此fragment将要插入的属于activity的父布局。savedInstanceState参数是一个Bundle,其中包含fragment之前保存的数据。

inflate()方法需要三个参数:

你想要填充的布局文件的resource ID 你填充的布局将要插入的ViewGroup。这个参数对于系统很重要,系统会根据它确定填充的布局的根视图的参数(此参数由ViewGroup指定) 一个boolean,表示将填充的布局插入到ViewGroup时是否依附于此ViewGroup。在本例中,这个参数为false,因为系统已经将此填充的布局插入到container中。如果设置为true,将会在最后的布局中创建额外的view group。

将fragment添加到activity中

通常情况下,fragment都作为宿主activity的UI的一部分,并存在于activity的视图层级中。向activity布局添加fragment有两种方法:

在activity的布局文件中声明fragment

    
    

标签的android:name属性指定了显示布局的fragment类。
系统在创建activity布局时,会实例化布局文件中指定的每个fragment,并调用它们的onCreateView()方法。系统会将此方法返回的View插入到标签所在的地方。

注意:每个fragment都需要指定一个id,当activity重启时,系统会用此id恢复fragment(你也可以用此id对fragment做一些操作,如移除fragment)。为fragment添加id有三种方式:

添加android:id属性 添加android:tag属性 如果上述两个数据都没有指定,系统会使用fragment容器的id
在ViewGroup中添加fragment
你可以在activity运行的任何时候向activity布局添加fragment。你只需要指定fragment将要放置在的ViewGroup即可。

如果要在activity中操作fragment,你需要使用FragmentTransaction的API。你可以用下例中的代码获取FragmentTransaction:

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

然后你就可以使用add()方法添加fragment了。代码如下:

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

add()方法的第一个参数是fragment将要插入到的ViewGroup,由resource ID指定,第二个参数是将要插入的fragment。
每次你对fragment做了操作,必须调用commit()方法。

添加没有界面的fragment

前面的例子说明了怎样添加有界面的fragment,然而,你也可以为activity添加没有界面的fragment,让它在后台做一些操作。

使用add(Fragment,String)方法可以为activity添加没有界面的fragment(这里的参数为string类型的“tag”,而不是视图ID)。使用这个方法添加fragment是不会收到onCreateView()回调的,所以你不需要实现此方法。

不单单没有界面的fragment可以使用string类型的tag指定,有界面的fragment也可以,只是没有界面的fragment只能用string类型的tag指定而已。如果你想要从activity中获取此没有界面的fragment,你需要调用findFragmentByTag()方法。


管理fragment

在activity中调用getFragmentManager()获取FragmentManager,可以管理此activity的fragment。

使用FragmentManager,你可以:

用findFragmentById()或findFragmentByTag()方法获取activity的某个fragment 用popBackStack()方法从回退栈中弹出fragment(模拟用户点击返回键) 用addOnBackStackChangedListener()方法注册一个监听器,以监听回退栈的变化

在前面的部分,我们也提到可以使用FragmentManager开始一个FragmentTransaction,从而进行添加、删除fragment等操作。
更多信息请看FragmentManager类的文档部分。


fragment事务

在activity中使用fragment有一个特性:你可以添加、删除、替换或对fragment进行其他操作,来作为对用户动作的响应。每个你提交给activity的fragment变化称为一个事务(transaction),你可以用FragmentTransaction中的API执行这些事务。你可以将事务添加到activity管理的回退栈中,从而允许用户撤销操作。

你可以用下列代码获取FragmentTransaction:

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

事务包含了你想要在一次事务中进行的若干操作。你可以用add()、remove()、replace()等方法为指定事务设置操作。然后,你可以用commit()方法将事务提交给activity。

在调用commit()方法之前,你可以调用addToBackStack()方法将事务加入到fragment事务的回退栈中。这个回退栈由activity管理,可以实现用户点击返回键后回到上一个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替换了原来的fragment,并将替换操作保存在回退栈中,当用户点击返回键时,此次替换将会被撤销,之前的fragment会重新显示。

如果你在一次事务中进行了多次操作,然后调用addToBackStack(),那么用户点击返回键后所有的操作都会被撤销。

操作在FragmentTransaction中的顺序不重要,但是注意:

最后调用commit() 如果你在同一个容器中添加了多个fragment,那么这些fragment在视图层级的顺序将取决于你添加的顺序

如果你在事务中移除了某个fragment,但是没有调用addToBackStack(),那么当此次事务被提交后,此fragment会被销毁,用户将不能返回到这个fragment。然而,如果你调用了addToBackStack(),那么此fragment会进入stopped状态,当用户返回时它会重新进入resumed状态。

注意:你可以在commit前调用setTransaction()方法为事务添加过渡动画。

调用commit()方法后事务不会立刻被执行,而是提交到activity的UI线程执行队列中,由线程空闲时执行。你可以在UI现场中调用executePendingTransaction()方法来立刻执行commit()方法提交的事务。通常你不需要这样做,除非在其他线程有工作依赖于此事务。

注意:你只能在用户离开activity前使用commit()提交事务,否则会抛出异常。这是为了防止在activity恢复后丢失提交的状态。如果你可以接受提交失败,那么可以调用commitAllowingStateLoss()方法。


与activity通信

虽然fragment的实现不依赖于某个activity,而且可以被用于多个activity中,但是fragment实例仍然跟它的宿主activity紧密的联系在一起。

fragment可以使用getActivity()方法访问宿主activity,从而可以很方便的执行一些任务,如在activity布局中寻找某个view:

View listView = getActivity().findViewById(R.id.list);

同样的,宿主activity可以调用FragmentManager的findFragmentById()或findFragmentByTag()方法获得Fragment实例:

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

在activity注册事件回调

在某些情况下,你可能需要fragment分享事件给activity。一个好的做法是:fragment定义一个回调接口,然后宿主activity实现它。当activity收到此回调时,它就可以将信息分享给其他fragment了。

例如,如果你的activity有两个fragment,一个显示文章列表(fragment A),另一个显示文章信息(fragment B)。那么fragment A必须通知activity某篇文章被选中了,这样activity才能通知fragment B显示此文章。在下面的例子中,fragment A声明了一个OnArticleSelectedListener接口:

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

然后activity实现OnArticleSelectedListener接口,并override其onArticleSelected方法,在此方法中activity将fragment A的信息通知给fragment B。为了确保宿主activity实现了此接口,fragment A可以在onAttach()方法中尝试实例化activity的OnArticleSelectedListener:

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");
        }
    }
    ...
}

如果activity没有实现此接口,那么fragment A会抛出ClassCastException。如果activity实现了此接口,那么fragment A就可以通过调用OnArticleSelectedListener的onArticleSelected方法向activity分享事件了。本例中,fragment A继承了ListFragment,每当用户点击了某个item,那么系统会通过onListItemClick()通知fragment A,然后fragment A调用onArticleSelected方法分享事件给activity:

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

为action bar添加item

你的fragment可以通过实现onCreateOptionsMenu()方法为activity的菜单(options menu)添加选项。为了让此方法被调用,你必须在onCreate()方法中调用setHasOptionsMenu(),来表明你的fragment会向菜单添加选项(否则你的fragment的onCreateOptionsMenu()方法不会被调用)。

你通过fragment添加的选择会被加入到已经存在的菜单中。当它添加的选择被点击时,fragment会通过onOptionsItemSelected()方法获取回调。

你也可以通过调用registerForContextMenu()方法将fragment布局中的view添加到系统菜单(context menu)中。当用户打开系统菜单时,fragment会在onCreateContextMenu()方法获取回调。当用户选择了某个选项时,fragment可以通过onContextItemSelected()方法获得回调。

注意:当用户点击菜单时,虽然你的fragment可以获取相关回调,但是activity会更早的获取回调。只有activity的回调没有处理此事件,你的fragment才会被通知


处理fragment生命周期

图3

管理fragment的生命周期跟管理activity的生命周期类似。fragment也存在三个状态:

resumed。表示此fragment在当前activity是可见的 paused。表示另一个activity在前台且拥有焦点,而fragment所在的activity仍然可见(前台activity部分透明或没有占满整个屏幕) stopped。表示此fragment不可见。可能是宿主activity被停止或者fragment被移除出activity并加入到回退栈中。处于stopped状态的fragment仍然存活,它的状态和信息仍然被系统保存着。然而此fragment已经对用户不可见,而且当activity被kill的时候它也会被kill

类似于activity,你可以用bundle保存fragment状态,用于activity进程被kill后重新创建时恢复fragment状态。你可以在fragment的onSaveInstanceState()方法保存状态,并在onCreate()方法、onCreateView()方法、onActivityCreated()方法恢复。

activity与fragment生命周期的不同主要体现在回退栈上。默认情况下,当activity被stopped的时候,会被压入系统管理的activity回退栈中。而fragment只有在移除fragment的事务中,主动要求加入回退栈(调用addToBackStack()方法),才会被压入宿主activity管理的回退栈中。

此外,管理fragment生命周期很像管理activity生命周期。你可以参考activity的生命周期管理文档。你真正需要在这个文档了解的,是activity的生命周期怎样影响fragment的生命周期。

注意:如果你的fragment需要Context对象,可以调用getActivity()方法。然而,只有当fragment已经attach了activity才能调用此方法。如果fragment没有attach了activity,或者已经detached,那么getActivity()会返回null

fragment与activity的生命周期关系

activity的生命周期会影响fragment的生命周期。例如,当activity调用onPause()时,每个fragment都会调用onPause()。

fragment有一些特有的生命周期回调方法,以处理与activity的独特的交互,比如创建或销毁fragment的UI。这些特有的回调方法包括:

onAttach()。当fragment与activity绑定的时候会调用 onCreateView()。创建fragment的界面时调用 onActivityCreated()。当activity的onCreate()方法返回后会调用 onDestroyView()。当fragment的界面被移除时调用 onDetach()。当fragment与activity解绑的时候调用

activity的生命周期与fragment的生命周期的关系,已经在图3中说明了。

activity进入resumed状态后,你可以自由的向此activity添加或移除fragment。因此,只有当activity进入resumed状态,fragment的生命周期变化才是独立的。

当activity退出resumed状态,fragment的生命周期就再次跟activity结合在一起。

点击复制链接 与好友分享!回本站首页
上一篇:Android 显示SVG格式图片
下一篇:Android Intent在Activity间的基本用法
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站