Fragment的详细解析

来源:互联网 发布:城乡统筹发展数据 编辑:程序博客网 时间:2024/05/18 01:09

介绍与应用

Fragment:可以理解为碎片,但也可以理解为片段。多个Fragment组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个Fragment。
Fragment有自己的生命周期,Fragment必须始终“嵌入”在 Activity 中使用,那么,它的生命周期受Activity的约束。只有在Activity处于活动状态才可以操作,如添加和移除。受约束的同时,fragment的生命的周期也是Activity同步的,当activity的 onPause() 方法被回调时,所在activity中的fragment也都会回调到 onPause().方法。如果该Activity 已停止,它内部没有碎片可以启动; 当活动被销毁,所有片段将被销毁。

Fragment是在Android 在 Android 3.0(API 级别 11)中引入的,主要是为了给大屏幕(如平板电脑、TV)上更加动态和灵活的 UI 设计提供支持。但目前来看,在开发过程中,其实也不只是应用在大屏幕中,手机也会经常应用,比如QQ、微信、新闻端、几乎多会用到,下面几个Tab用来切换Fragment;指导界面或者广告位的切换,也用到Fragment,已成为程序员必备技能。

关于导包:
Fragment是在3.0版本引入的,如果你使用的是3.0之前的系统,需要先导入android-support-v4的jar包才能使用Fragment功能:

import android.support.v4.app.Fragment;import android.support.v4.app.FragmentManager;

如果是在Android 3.0以上的版本使用,可以导入:

import android.app.Fragment;

但是,目前来看几乎很少使用那么低版本的系统,所以导包看项目而定吧。如果不会在Android 3.0以下使用,建议导入后者,对于初学者,避免不必要的麻烦,而且属性动画也是3.0才引用的,如果需要使用属性动画,更需要导入app下的包,(以下的测试导入的是:import android.app.Fragment;)

如何使用Fragment

开始创建Fragment

Fragment的使用与Activity非常相似,Activity需要继承的是Activity,而Fragment继承的是Fragment,而且多需要实现类似的回调方法 ,如 onCreate()、onStart()、onPause() 。其中一个不同的地方是必须使用 onCreateView() 的回调来定义Layout。

代码开始撸起来吧,如下:

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

onCreateView( ) : 系统会在Fragment首次绘制其用户界面时调用此方法。 要Fragment绘制 UI,此方法中返回的 View 必须是Fragment布局的根视图。如果片段未提供 UI,可以返回 null。
container :是Fragment布局将插入到的父 容器(来自 Activity 的布局);
savedInstanceState: 是在恢复Fragment时,提供上一Fragment实例相关数据的 Bundle;

其中,R.layout.fragment_first 是Fragment对应的布局文件。

向Activity添加Fragment

Fragment作为 Activity 总体视图层次结构的一部分嵌入到 Activity 中。可以通过两种方式向 Activity 布局添加片段:

a. 使用XML添加fragment到activity(静态添加)

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.gotechcn.testfagment.MainActivity">    <fragment        android:id="@+id/first_fragment"        android:name="com.gotechcn.testfagment.FirstFragment"        android:layout_width="match_parent"        android:layout_height="match_parent">    </fragment></RelativeLayout>

其中fragment 标签中的 android:name 属性指定要在布局中实例化的 Fragment 类。
是的,在xml文件中静态加入,就是那么的简单。但是,虽然简单,在运行的过程中可能也有一些坑:
1.出现 ClassCastException 异常
fragment可以重用,是模块化的组件,每一个fragment的实例都与父类FragmentActivity有依赖,你可以通过定义每一个fragment的XML来实现这样的联系。 FragmentActivity 是一个在Support Library中的特殊的activity,使用它来向Level 11以下的系统进行兼容。所以,你要是导入是v4的包,那么你当前的FirstActivity必须继承FragmentActivity;如果是在app下的包,则可以继承通常的 Activity。同时,获取FragmentManager对象时,在v4包下是通过getSupportFragmentManager获取的。(这也是我上面说的,可以的话,建议不导入v4包)

2.出现IllegalArgumentException异常
每个fragment都需要一个唯一的标识符,重启 Activity 时,系统通过使用该标识符来恢复片段。所以,有可能在布局文件里面没有指定id, 可以通过三种方式为片段提供 ID:
为 android:id 属性提供唯一 ID。
为 android:tag 属性提供唯一字符串。
如果您未给以上两个属性提供值,系统会使用容器视图的 ID。

b. 运行时添加fragment到activity(动态添加)
在Activity运行时中执行片段事务(如添加、移除或替换片段),必须使用 FragmentManager 来创建一个FragmentTransaction,通过它来执行Fragment事务的操作,并且提交事务,MainActivity的代码如下:

 @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        FragmentManager fragmentManager = getFragmentManager();        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();        fragmentTransaction.add(R.id.layout, new FirstFragment());        fragmentTransaction.commit();    }

其中,add() 的第一个参数是 ViewGroup,即应该放置fragment的位置,由资源 ID 指定,第二个参数是要添加的fragment。
一旦您通过 FragmentTransaction 做出了更改,就必须调用 commit() 以使更改生效

对于实际的开发中,最为常用的是第二种方式。

Fragment的事务管理

前面也使用过,Fragment是通过FragmentManager来管理的,可以通过Activity 调用 getFragmentManager()获取。

有了事务管理,可以使用 add()、remove() 和 replace() 等方法为给定事务设置你想要执行的所有更改。最后记得必须调用 commit(),事务才会有效执行。
当你执行fragment的置换或者移除等切换动作时请注意:因为用户很可能想做后退与撤销的动作,为了让用户能够回退到之前的状态,必须在你commit FragmentTransaction 之前执行 addToBackStack(),如:

transaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);transaction.commit();

其中,addToBackStack() 的方法会需要一个可选的string参数来指定这个特定的动作。除非你需要使用FragmentManager.BackStackEntry的API来做一些更加高级复杂的操作,一般是不需要传递的。

当你执行移除或者置换操作并且把这个动作添加到back stack的时候,被移除的fragment并没有被销毁而是stopped状态。如果用户执行回退的操作来恢复那个fragment,它会被restart。如果你没有把那个动作添加到back stack,那么fragment会被销毁。

通过事务,还可以执行以下操作:

  • 对于每个片段事务,您都可以通过在提交前调用 setTransition() 来应用过渡动画。
  • 通过findFragmentById()(对于在 Activity 布局中提供 UI 的片段)或findFragmentByTag()(对于提供或不提供 UI 的片段)获取 Activity 中存在的fragment;
  • 通过 addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器。
  • 通过 popBackStack()(模拟用户发出的BACK命令)将fragment从返回栈中弹出。

Fragment与Activity的通信

在使用的过程中,Fragment与Activity、Fragment与Fragment多需要交互通信,那么他们是怎样交互的呢?
两个fragment之间没有办法直接交互,所有的Fragment-to-Fragment之间的交互都是基于activity进行操作的。

一般他们之间的通信可以使用这2种方式:

第一种:
当Fragment需要获取它所在的Activity,可以通过 getActivity() 访问 Activity 实例,并轻松地执行在 Activity 布局中查找视图等任务,如

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

当 Activity 获取它包含的Fragment,调用Activity关联的FragmentManager ,使用 findFragmentById() 或 findFragmentByTag(),获取需要的Fragment之后,再执行其方法,例如:

FirstFragment fragment = (FirstFragment) getFragmentManager().findFragmentById(R.id.first_fragment);fragment.cleanFoucs();

第二种:

执行该操作的一个好方法是,在Fragment内定义一个回调接口,并要求宿主 Activity 实现它。这样Fragment可以调用改回调方法将数据传给Activity, 当 Activity 通过该接口收到回调时,可以根据需要与布局中的其他片段共享这些信息。以下是官方的一个例子:

public class HeadlinesFragment extends ListFragment {    OnHeadlineSelectedListener mCallback;    // Activity必须实现该接口    public interface OnHeadlineSelectedListener {        public void onArticleSelected(int position);    }    @Override    public void onAttach(Activity activity) {        super.onAttach(activity);        try {            mCallback = (OnHeadlineSelectedListener) activity;        } catch (ClassCastException e) {            throw new ClassCastException(activity.toString()                    + " must implement OnHeadlineSelectedListener");        }    }}

为确保宿主 Activity 实现此接口,Fragment 的 onAttach() 回调方法(系统在向 Activity 添加片段时调用的方法)会通过转换传递到 onAttach() 中的 Activity 来实例化 OnHeadlineSelectedListener 的实例。

接口的方法会在用户点击listitem的时候被call到。Fragment使用这个callback接口来传递事件给父activity。

@Overridepublic void onListItemClick(ListView l, View v, int position, long id) {    // 发送事件给宿主Activity    mCallback.onArticleSelected(position);}

这样子,Fragment就完事了,Activity只要实现该接口就OK了:

public static class MainActivity extends Activity        implements HeadlinesFragment.OnHeadlineSelectedListener{    public void onArticleSelected(int position) {        // 做一些事...    }}

Fragment的生命周期

Fragment的生命周期与Activity很相似(不熟悉Activity生命周期的可以参考这里),主要也是以这三种状态存在:

  • Resumed:Fragment在运行中的 Activity 中可见;
  • Paused:另一个 Activity 位于前台并具有焦点,但此Fragment所在的 Activity 仍然可见(前台 Activity 部分透明,或未覆盖整个屏幕)。
  • Stopped: Fragment不可见。宿主 Activity 已停止,或Fragment已从Activity 中移除,但已添加到返回栈。 停止Fragment仍然处于活动状态(系统会保留所有状态和成员信息)。 不过,它对用户不再可见,如果Activity 被终止,它也会被终止。

前面说到,Activity的生命周期会影响Fragment的生命周期,而且具体流程又是非常相似的。有点抽象,那我们用实践来看看他们具体的生命流程。
实践中,最好是和Activity一起比较,这样能够清晰看到的区别:

Fragment的代码如下:

public class FirstFragment extends Fragment{    public static final String TAG = "Fragment Life";    @Override    public void onAttach(Context context)    {        Log.e(TAG,"onAttach()");        super.onAttach(context);    }    @Override    public void onCreate( Bundle savedInstanceState)    {        Log.e(TAG,"onCreate");        super.onCreate(savedInstanceState);    }    @Override    public View onCreateView(LayoutInflater inflater,  ViewGroup container,  Bundle savedInstanceState)    {        Log.e(TAG,"onCreateView");        return inflater.inflate(R.layout.fragment_first, container, false);    }    @Override    public void onActivityCreated( Bundle savedInstanceState)    {        Log.e(TAG,"onActivityCreated");        super.onActivityCreated(savedInstanceState);    }    @Override    public void onStart()    {        Log.e(TAG,"onStart");        super.onStart();    }    @Override    public void onResume()    {        Log.e(TAG,"onResume");        super.onResume();    }    @Override    public void onPause()    {        Log.e(TAG,"onPause");        super.onPause();    }    @Override    public void onStop()    {        Log.e(TAG,"onStop");        super.onStop();    }    @Override    public void onDestroyView()    {        Log.e(TAG,"onDestroyView");        super.onDestroyView();    }    @Override    public void onDestroy()    {        Log.e(TAG,"onDestroy");        super.onDestroy();    }    @Override    public void onDetach()    {        Log.e(TAG,"onDetach");        super.onDetach();    }}

其布局很简单,如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:orientation="vertical"              android:background="#66006600"              android:layout_width="match_parent"              android:layout_height="match_parent">    <TextView        android:textSize="30sp"        android:text="First Fragment"        android:gravity="center"        android:textColor="@android:color/white"        android:layout_width="match_parent"        android:layout_height="match_parent"/></LinearLayout>

Activity的代码如下:

public class MainActivity extends Activity{    private static final String TAG = "Activity Life";    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Log.e(TAG, "onCreate()...");    }    @Override    protected void onRestart() {        super.onRestart();        Log.e(TAG, "onRestart()...");    }    @Override    protected void onStart() {        // The activity is about to become visible.        super.onStart();        Log.e(TAG, "onStart()...");    }    @Override    protected void onResume() {        // The activity has become visible (it is now "resumed").        super.onResume();        Log.e(TAG, "onResume()...");    }    @Override    protected void onPause() {        // Another activity is taking focus (this activity is about to be "paused").        super.onPause();        Log.e(TAG, "onPause()...");    }    @Override    protected void onStop() {        // The activity is no longer visible (it is now "stopped")        super.onStop();        Log.e(TAG, "onStop()...");    }    @Override    protected void onDestroy() {        // The activity is about to be destroyed.        super.onDestroy();        Log.e(TAG, "onDestroy()...");    }}

其布局是将Fragment添加进来,如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal"    tools:context="com.gotechcn.testfagment.MainActivity">    <fragment        android:id="@+id/first_fragment"        android:name="com.gotechcn.testfagment.FirstFragment"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_weight="1">    </fragment></LinearLayout>

好了,测试代码已经准备好了,我们运行起来看看log

首先,直接运行,其生命周期:
这里写图片描述

接着,按Home键,将应用暂停,其生命周期:
这里写图片描述

然后,再点击应用唤醒,其生命周期:
这里写图片描述

最后,按返回键,关闭应用,其生命周期:
这里写图片描述

如果了解Activity的生命周期,相信大家对于这个,一看也就明白了。是的,几乎是相同的流程,这不足为怪,Fragment需要嵌入在Activity才能运行,相同的生命周期更好处理他们 的关系。

官方也有形象的生命周期流程图,如图所示:

Fragment生命周期流程图

稍微总结一下他们的流程的区别:

相同点:

可以看出,Fragment所在的 Activity 的生命周期会直接影响Fragment的生命周期,其表现为,Activity 的每次生命周期回调都会引发每个Fragment的类似回调。 例如,当 Activity 收到 onPause() 时,Activity 中的每个Fragment也会收到 onPause()。

在其他方面,管理Fragment生命周期与管理 Activity 生命周期非常相似。 因此,管理 Activity 生命周期的做法同样适用于Fragment。

对于状态的保存也是一样的,假使 Activity 的进程被终止,而需要在重建 Activity 时恢复Fragment状态,也可以使用 Bundle 保留Fragment的状态。您可以在Fragment的 onSaveInstanceState() 回调期间保存状态,并可在 onCreate()、onCreateView() 或 onActivityCreated() 期间恢复状态。

不同点:

其中,以下生命周期是与Activity不同的:

  • onAttach(Content) :在Fragment已与 Activity 关联时调用(Content传递到此方法内)。

  • onCreateView(LayoutInflater, ViewGroup, Bundle)创建并返回与该Fragment相关联的视图。

  • onActivityCreated(Bundle):在 Activity 的 onCreate() 方法已返回时调用。

  • onDestroyView() 在移除与Fragment关联的视图层次结构时调用。

  • onDetach() 在取消Fragment与 Activity 的关联时调用。

  • onDestroy() 所谓做片段的状态的最后的清理工作。

Activity 生命周期与Fragment生命周期之间的最显著差异在于它们在其各自返回栈中的存储方式。 默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈(以便用户通过返回按钮回退到 Activity)。而Fragment,仅当在移除Fragment的事务执行期间通过显式调用 addToBackStack() 保存实例时,系统才会将片段放入由宿主 Activity 管理的返回栈。

Fragment的特点大致如下:
Fragment可以被多个Activity重复的利用;
Fragment可以用通过FragmentManager管理,进行动态的添加、移除、替换等操作;
Fragment作为Activity的组成部分,其通信交互是通过调用getActivity()获取当前的Activity,Activity通过FragmentManager调用findFragmentById()来获取Fragment,或者通过回调的方式通信;
Fragment可以响应自己的事件,拥有自己的生命周期,但同时又被宿主Activity的生命周期影响;

好了,Fragment的介绍就到这里了。每次总结完,对于还是菜鸟的我来说,有那么一瞬间找到大牛的感觉,哈哈。

如有错误,欢迎指出!

原创粉丝点击