原文出处:http://www.ccbu.cc/index.php/android/fragment-lifecycle.html

什么是Fragment

Fragment实在是Android 3.0(API 11)中引入的,译作“碎片”。Fragment作为应用用户接口或行为的一部分而放置在Activity中。Fragment不能独立存在,只能依赖与Activity存在。Fragment拥有自己的生命周期,其生命周期受宿主Activity的控制,状态会随着Activity的状态的改变而发生改变。

因Android各个版本的Fragment有所差异,同时为了兼容低版本,support-v4库中提供了一套兼容Fragment API,最低兼容Android 1.6。

过去support-v4库是一个jar包,24.2.0版本开始,将support-v4库模块化为多个jar包,包含:support-fragment, support-ui, support-media-compat等,这么做是为了减少APK包大小,你需要用哪个模块就引入哪个模块。

Fragment的特点

Fragment是为了解决碎片态的用户界面而产生的,在处理UI方面有者自己独特的优势。Fragment拥有自己的生命周期,可以处理用户输入事件。一个Activity中可以拥有多个Fragment,一个Fragment可以被多个Activity重用。在Activity中可以动态的添加,删除,和管理Fragment。

基于上面所述的优势,可以看出,Fragment拥有以下以下特点:

  • 独立性 Fragment拥有独立的生命周期,可以独立处理所有的用户交互事件
  • 模块化 将某一功能封装到一个Fragment中供Activity使用
  • 复用性 一个Fragment可以被多个Activity复用
  • 灵活性 Fragment不但可以按照特定功能进行单独封装,还可以在Activity中进行动态的添加,删除和管理。

Fragment生命周期

上面多次提到Fragment拥有自己的生命周期,那他的生命周期是怎么样的呢,先看一下经典的Fragment生命周期图。

其中,各个生命周期函数的说明如下:

  • onAttach():Fragment和Activity相关联时调用。可以通过该方法获取 Activity引用,还可以通过getArguments()获取参数。
  • onCreate():系统在Fragment被创建时调用。
  • onCreateView():创建Fragment的布局,如果片段未提供 UI,您可以返回 null。
  • onActivityCreated():当Activity完成onCreate()时调用。
  • onStart():当Fragment可见时调用。
  • onResume():当Fragment可见且可交互时调用。
  • onPause():当Fragment不可交互但可见时调用。
  • onStop():当Fragment不可见时调用。
  • onDestroyView():当Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁Fragment时调用。
  • onDetach():当Fragment和Activity解除关联时调用。

Fragment的是依于Activity而存在的,从他的生命周期图可以看出,Fragment的生命周期与Activity的生命周期是十分相似的。下图展示了fragment的生命周期与Activity生命周期函数的对应关系。

Fragment的使用

下面通过Fragment的使用实例了解在实际应用中如何来使用Fragment构建UI,同时通过这些使用实例,进一步的理解其生命周期。

1. 创建一个新的Fragment

创建一个自己的Fragment,只需要继承系统的Fragment或support-v4库中的Fragment类,或者是继承Fragment的子类,并实现需要的方法即可。其中一个默认的不带参数的构造函数,onCreateView()函数是必须的。onCreate,onResume,onPause()等方法也是十分常用的。

public class BlankFragment extends Fragment {        public BlankFragment() {        // Required empty public constructor    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,                             Bundle savedInstanceState) {        // Inflate the layout for this fragment        return inflater.inflate(R.layout.fragment_blank, container, false);    }}

Fragment作为Activity用户界面的一部分,支持View操作是其最基本的功能;其中onCreateView用来加载Fragment的布局,返回值即为加载进来的View。一般onCreateView通过加载XML布局文件来加载自己的布局,onCreateView提供了加载布局的LayoutInflater,专门供使用者来加载布局。

Fragment还有几个常用的子类,可以帮助我们来更方便的实现想要的特定功能的Fragment。包括以下几个。

  • DialogFragment 显示浮动对话框。使用此类创建对话框可有效地替代使用 Activity 类中的对话框帮助程序方法,因为您可以将片段对话框纳入由 Activity 管理的片段返回栈,从而使用户能够返回清除的片段。
  • ListFragment 显示由适配器(如 SimpleCursorAdapter)管理的一系列项目,类似于 ListActivity。它提供了几种管理列表视图的方法,如用于处理点击事件的 onListItemClick() 回调。
  • PreferenceFragment 以列表形式显示 Preference 对象的层次结构,类似于 PreferenceActivity。这在为您的应用创建“设置” Activity 时很有用处。

2.静态加载Fragment

静态加载Fragment即是将Fragment像其他普通View一样,直接写在layout的xml文件中。View加载时会自动进行加载。

<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=".MainActivity">    <fragment        android:name="cc.ccbu.canvassimple.BlankFragment"        android:layout_width="match_parent"        android:layout_height="match_parent" />RelativeLayout>

在布局文件中使用fragment标签进行Fragment的添加,并指明android:name属性制定对应的Fragment类。当系统创建此 Activity 布局时,会实例化在布局中指定的每个片段,并为每个片段调用 onCreateView() 方法,以检索每个片段的布局。系统会直接插入片段返回的 View 来替代 元素。

每个片段都需要一个唯一的标识符,重启 Activity 时,系统可以使用该标识符来恢复片段(您也可以使用该标识符来捕获片段以执行某些事务,如将其移除)。 可以通过三种方式为片段提供 ID:
1.为 android:id 属性提供唯一 ID。
2.为 android:tag 属性提供唯一字符串。
3.如果您未给以上两个属性提供值,系统会使用容器视图的 ID。

3.动态加载fragment

静态方式加载Fragment的方式还是显得不够灵活,所以在 Activity 中我们还可以可以根据需要来执行Fragment的添加、移除、替换以及其他操作。 提交给 Activity 的每组更改都称为事务,我们可以通过使用 FragmentTransaction 的 API 来执行一项事务。每次可以将每个事务保存到由 Activity 管理的返回栈内(addToBackStack),从事使能够回退片到之前保持的状态。

Fragment newFragment = new BlankFragment();FragmentTransaction transaction = getFragmentManager().beginTransaction();transaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);transaction.commit();

上例中,newFragment 会替换在 R.id.fragment_container ID 所标识的布局容器中的内容;通过调用 addToBackStack() 可将事务保存到返回栈,当用户按返回键时,会执行撤销事务,如果返回栈有之前保存的事务项,则以出栈的形式回退到上一个事务项状态。

如果向FragmentTransaction添加了多个更改(如add()remove()),并且调用了 addToBackStack(),则在调用commit() 前应用的所有更改都将作为单一事务添加到返回栈,并且返回按钮会将它们一并撤消。

对于每个片段事务,您都可以通过在提交前调用 setTransition() 来应用过渡动画。

4.添加菜单项

Fragment可以通过实现onCreateOptionsMenu()项Activity的OptionsMenu中添加菜单项。不过需要注意的是,必须在 onCreate() 期间调用 setHasOptionsMenu(),Fragment的onCreateOptionsMenu方法才会被调用。在Fragment中添加的所有菜单项都会被追加到现有菜单项之后。当菜单项被选中时,Fragment会收到对应的 onOptionsItemSelected() 回调。

尽管您Fragment会收到其添加的每个菜单项对应的菜单项回调,但当用户选中菜单项时,Activity 会首先收到相应的回调。 如果 Activity 对菜单项回调的实现不会处理该菜单项,则系统会将事件传递到Fragment的回调。

Fragment通信

1.给Fragment设置参数

如果在创建Fragment时要传入参数,必须要通过setArguments(Bundle bundle)方式添加,而不建议通过为Fragment添加带参数的构造函数,因为通过setArguments()方式添加,在由于内存紧张导致Fragment被系统杀掉并恢复(re-instantiate)时能保留这些数据。使用setArguments,在创建Fragment的时候传递参数,然后在fragment的onCreate方法处获取参数,但是需要注意的是setArguments()方法必须在fragment创建以后,add之前调用。

public static TestFragment newInstance(String str){        Bundle bundle = new Bundle();        bundle.putString("info", str);        TestFragment fragment = new TestFragment();        fragment.setArguments(bundle);        return fragment;    }

2.获取实例

Fragment可以通过getActivity方法来获取Activity的实例,从而执行Activity相关的UI操作。但使用getActivity前需要注意Fragment的生命周期与Activity生命周期的对应关系,在部分生命周期函数内调用getActivity是不会返回有效的实例,获取到的是null值。

同样的,Activity中也可以通过调用 findFragmentById() 或 findFragmentByTag()方法,从 FragmentManager 获取Fragment 引用,进而来访问Fragment相应的方法。

3.使用回调方法

有的时候,我们可以通过使用回调方法来实现Activity与Fragment的通信操作。

public class BlankFragment extends Fragment {    ...    private OnItemClickListener mItemClickListener = null;    @Override    public void onAttach(Activity activity) {        super.onAttach(activity);        try {        mItemClickListener = (OnItemClickListener)activity;        } catch (ClassCastException e) {            throw new ClassCastException(activity.toString() + " must implement OnItemClickListener");        }    }        public interface OnItemClickListener {        void onItemClick(int itemId);    }}
public class MainActivity extends Activity implements BlankFragment.OnItemClickListener {    ...    @Override    public void onItemClick(int itemId) {        Log.d(TAG, "onItemClick : " + itemId);    }}

上面的例子中,BlankFragment定义了OnItemClickListener接口,宿主Activity必须要实现该接口,在BlankFragment的onAttach方法中,通过强制类型转换,将Activity参数转为OnItemClickListener接口,如果宿主Activity没有实现该接口,会抛出ClassCastException异常。

Fragment懒加载

懒加载主要用于ViewPager加载Fragment页面的情况,且ViewPager的每个子页面都是一个Fragment。默认情况下,ViewPager会执行预加载来提前加载一些页面来使得UI左右滑动效果更加流畅。ViewPager可以通过setOffscreenPageLimit(int limit)设置预加载页面数量,但有一个最小限制,保证知识加载两个或三个页面。在一些场景下,当ViewPager中的页面不可见时,我们不希望他来加载数据时,那么此时我们就需要通过懒加载方式来加载数据。懒加载的方式库提供应用的初始化速度,同时也可以避免不必要的资源加载。

那Fragment的懒加载该如何实现呢,这里首先来认识一个方法setUserVisibleHint(boolean isVisibleToUser)。该方法中的isVisibleToUser参数用来表示当前Fragment是否对用户可见。当Fragment对用户可见或不可见时,该方法都会被调用。所以我们可以依据该方法,在Fragment对用户可见时在去加载数据。但需要注意的一点是,setUserVisibleHint(boolean isVisibleToUser)方法会多次回调,而且可能会在onCreateView()方法执行完毕之前回调。所以,在加载数据前,必须满足两个条件:

  1. setUserVisibleHint(boolean isVisibleToUser)参数为true。
  2. onCreateView()方法已经执行
  3. 数据还未被加载

我们可以把这些操作封装到一个基类里。

public abstract class LazyLoadFragment extends Fragment {    private boolean isUserVisible = false;    private boolean isViewCreated = false;    private boolean isDataLoaded = false;    @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        isUserVisible = isVisibleToUser;        onLazyLoad();    }    @Override    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        isViewCreated = true;        onLazyLoad();    }    public void onLazyLoad() {        if (!isDataLoaded && isUserVisible && isViewCreated) {            loadData();        }    }    public abstract boolean loadData();}

在子类中只用实现loadData()方法来加载数据即可。

更多相关文章

  1. SQLite数据库增删改查操作
  2. Android线程—HandlerThread的使用及原理
  3. Android拍照流程
  4. Android与H5交互,向H5注入APP账号密码免登录等。
  5. 安卓笔记-视频版(还没学完)
  6. 如何让 android 完全退出
  7. android Activity 之 startActivityForResult 的使用
  8. 安卓开发之 在应用中使用数据库
  9. Android(安卓)VOIP拨打电话机制分析

随机推荐

  1. Android Realm数据库使用总结及采坑记录
  2. Android与JS之间的互调
  3. Slides for RxJava,Android,FRP
  4. Android 检查网络状态是否可用 (工具类总
  5. CentOS 下载 Android 源代码。
  6. Android经典动画案例分析
  7. Android中Activity触摸事件传递源码学习
  8. 常用的系统调用【Android】
  9. Android创建和配置布局动画
  10. Android权限申请:自带方法 + 第三方库实现