关于Fragment
文章目录
前言
碎片(Fragment)是一种可以嵌入到活动(Activity)当中的UI片段,它能够让程序更加合理的充分利用更大的屏幕空间。它有自己的生命周期,有自己的布局,能接收自己的输入事件。
可以将多个片段组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,可以在 Activity 运行时添加或移除片段。
片段并非必须成为 Activity 布局的一部分;您还可以将没有自己 UI 的片段用作 Activity 的不可见工作线程。
一、使用方式
xml布局方式加载
当系统创建此 Activity 布局时,会实例化在布局中指定的每个片段,并为每个片段调用 onCreateView() 方法,以检索每个片段的布局。系统会直接插入片段返回的 View 来替代 元素。
注:每个片段都需要一个唯一的标识符,重启 Activity 时,系统可以使用该标识符来恢复片段(您也可以使用该标识符来捕获片段以执行某些事务,如将其移除)。 可以通过三种方式为片段提供 ID:
为
android:id
属性提供唯一 ID。
为android:tag
属性提供唯一字符串。
如果您未给以上两个属性提供值,系统会使用容器视图的 ID。
<fragment
android:id="@+id/ft"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.hdib.MainFragment"/>
// Activity中获取Fragment实例
Fragment fragment = getSupportFragmentManager().findFragmentByTag("ftTag");
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.ft);
//Fragment中获取Activity实例
MainActivity ma = (MainActivity)getActivity();
动态加载
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
fl = new FragmentLeft();
transaction.replace(R.id.fl, fl);
//transaction.replace(R.id.fl, fl,"fltag");
//将Fragment加入堆栈管理,参数为栈名称,这样点击返回按钮页面会返回到替换前的状态
transaction.addToBackStack(null);
transaction.commit();
二、生命周期
生命周期方法
方法 | 描述 |
---|---|
onAttach(Context) onAttach(Activity)
|
在Fragment已与 Activity 关联时调用(Activity 传递到此方法内)。后者已经过时了,可以在前者中通过getActivity() 方法获取Activity。注意:如需 Fragment 内的某个 Context 对象,可以调用 getActivity()。但要注意,请仅在片段附加到 Activity 时调用 getActivity()。如果片段尚未附加,或在其生命周期结束期间分离,则 getActivity() 将返回 null。 |
onCreate() |
创建Fragment对象,Fragment从无到有时,执行该方法,初始化操作在此方法中进行 |
onCreateView() |
调用它可创建与片段关联的视图层次结构。如果是调用add(ft, tag) 方法添加无UI的Fragment,则不会调用该方法。该方法是在 Activity的onCreate() 方法返回后才开始调用的。 |
onActivityCreated() |
在 Activity 的 onCreate() 方法已经返回时调用。如果 Activity早已存在,如在Activity中点击按钮加载Fragment,那么该方法也一定会执行。 |
onStart() |
同Activity的方法功能一致,或者跟随Activity的该方法执行。 |
onResume() |
同Activity的方法功能一致,或者跟随Activity的该方法执行。 |
onPause() |
同Activity的方法功能一致,或者跟随Activity的该方法执行。 |
onStop() |
同Activity的方法功能一致,或者跟随Activity的该方法执行。 |
onDestroyView() |
在移除与片段关联的视图层次结构时调用。 |
onDestroy() |
销毁Fragment对象时,执行该方法。 |
onDetach() |
在取消片段与 Activity 的关联时调用。 |
Activity和Fragment生命周期
在Activity的onCreate方法中创建Fragment并replace,然后按返回键销毁Activity,日志如下:
MyFragment() 构造方法执行
onAttach aaa@qq.com
MyFragment onCreate
Activity onCreate finish
MyFragment onCreateView
MyFragment onViewCreated
MyFragment onActivityCreated
MyFragment onStart
Activity onStart
Activity onResume
MyFragment onResume
MyFragment onPause
Activity onPause
MyFragment onStop
Activity onStop
MyFragment onDestroyView
MyFragment onDestroy
MyFragment onDetach
Activity onDestroy
Fragment管理方法
//获取android.app.FragmentManager
FragmentManager fragmentManager = getFragmentManager();
//获取 android.support.v4.app.FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
方法 | 所属类 | 描述 |
---|---|---|
addToBackStack(stackName) |
FragmentTransaction | 将Fragment加入堆栈管理,参数为栈名称,这样点击返回按钮页面会返回到上一个栈元素。 |
add(layoutId,ft) |
FragmentTransaction | 将Fragment加入指定的ViewGroup中,就像加入一个View一样,如果该ViewGroup中已经有一个Fragment,该操作并不会影响这个Fragment的生命周期,因为这只相当于在一个View上又盖了一个View。 注意:如果该操作同时执行了 addToBackStack() 那么操作本身也会被记录到栈中,执行返回时就返回到了add(layoutId,ft) 前的状态。
|
add(ft, tag) |
FragmentTransaction | 添加没有UI的Fragment,建议使用该方法。由于它并不与 Activity 布局中的视图关联,因此不会收到对 onCreateView() 的调用。因此,您不需要实现该方法。注意:如果该操作同时执行了 addToBackStack() 那么操作本身也会被记录到栈中,执行返回时就返回到了add(ft,tag) 前的状态。
|
remove(ft) |
FragmentTransaction | 移除指定的Fragment。 对于入栈的Fragment,只是移除View(从 getFragments() 列表移除),不会将该Fragment出栈,也不会销毁该Fragment。该操作会使被移除的Fragment依次执行生命周期方法:onPause() 、onStop() 、onDestroyView() 。对于没有入栈的Fragment,会同时销毁该Fragment,执行 onDetach() 、onDestroy() 方法。注意:如果该操作同时执行了 addToBackStack() 那么操作本身也会被记录到栈中,执行返回时就返回到了remove(ft) 前的状态。
|
detach(ft) |
FragmentTransaction | 将指定的Fragment与Activity解除关联。 无论Fragment是否入栈,该操作都只是移除View(从 getFragments() 列表移除),不会将该Fragment出栈,也不会销毁该Fragment。该操作会使被移除的Fragment依次执行生命周期方法:onPause() 、onStop() 、onDestroyView() 。注意:如果该操作同时执行了 addToBackStack() 那么操作本身也会被记录到栈中,执行返回时就返回到了detach(ft) 前的状态。
|
attach(ft) |
FragmentTransaction | 添加关联。只对已经解除关联的Fragment才有效。 注意:如果该操作同时执行了 addToBackStack() 那么操作本身也会被记录到栈中,执行返回时就返回到了detach(ft) 前的状态。
|
replace(layoutId,ft) |
FragmentTransaction | 先移除指定ViewGroup中的所有元素,然后再添加指定Fragment到该ViewGroup中。相当于n次remove(ft) 加一次add(layoutId,ft,tag) 操作。注意:如果该操作同时执行了 addToBackStack() 那么操作本身也会被记录到栈中,执行返回时就返回到了replace(layoutId,ft) 前的状态。
|
show(ft) |
FragmentTransaction | 只是显示Fragment,相当于View的setVisibility(View.VISIBLE) ,不影响Fragment的生命周期。只对已经隐藏的Fragment才有效。注意:如果该操作同时执行了 addToBackStack() 那么操作本身也会被记录到栈中,执行返回时就返回到了show(ft) 前的状态。
|
hide(ft) |
FragmentTransaction | 只是显示Fragment,相当于View的setVisibility(View.GONE) ,不影响Fragment的生命周期。只对已经显示的Fragment才有效。注意:如果该操作同时执行了 addToBackStack() 那么操作本身也会被记录到栈中,执行返回时就返回到了hide(ft) 前的状态。
|
commit() |
FragmentTransaction | 提交事务操作,以使操作生效。 注意:您只能在 Activity 保存其状态(用户离开 Activity)之前使用 commit() 提交事务。如果您试图在该时间点后提交,则会引发异常。 这是因为如需恢复 Activity,则提交后的状态可能会丢失。 对于丢失提交无关紧要的情况,请使用 commitAllowingStateLoss() 。这样如果在onSaveInstanceState()之后,你可能会丢失掉FragmentManager的状态, 即save之后任何被添加或被移除的Fragments。
|
commitNow() |
FragmentTransaction | 提交事务操作,并立即执行操作。v24.0.0 以后提供的方法。注意:您只能在 Activity 保存其状态(用户离开 Activity)之前使用 commit() 提交事务。如果您试图在该时间点后提交,则会引发异常。 这是因为如需恢复 Activity,则提交后的状态可能会丢失。 对于丢失提交无关紧要的情况,请使用 commitNowAllowingStateLoss() 。这样如果在onSaveInstanceState()之后,你可能会丢失掉FragmentManager的状态, 即save之后任何被添加或被移除的Fragments。
|
FragmentManager beginTransaction() |
FragmentManager | 获取事务操作对象,以便进行事务操作(添加、移除、替换Fragment等)。 |
popBackStack() |
FragmentManager | 将栈顶Fragment出栈,模拟用户发出返回命令。这个操作将销毁这个Fragment,执行生命周期方法:onDestroy() 、onDetach() 。如果Fragment正处于显示状态还会执行: onPause() 、onStop() 、onDestroyView()
|
getBackStackEntryCount() |
FragmentManager | 获取返回栈中元素(Fragment)数量。 |
boolean executePendingTransactions() |
FragmentManager | 将所有pending在队列中还有你新提交的transactions都立即执行。通常不必这样做,除非其他线程中的作业依赖该事务。 |
findFragmentById(id) findFragmentByTag(tag)
|
FragmentManager | 查找指定Fragment。 |
addOnBackStackChangedListener() |
FragmentManager | 注册一个侦听返回栈变化的侦听器。 |
List<Fragment> getFragments() |
FragmentManager | 获取已添加到栈中的Fragment列表,该列表实际上是由两个集合来管理的,mAdded 和mActive ,前者是已经被add() 的Fragment集合,后者是处于attach() 状态的Fragment索引的集合。 |
putFragment(bundle, key, ft) |
FragmentManager | 存储Fragment的索引到Bundle中,存储和恢复数据时使用 |
Fragment getFragment(bundle,key) |
FragmentManager | 根据Bundle中得到的索引值获取对应的Fragment,存储和恢复数据时使用 |
三、与 Activity 通信
Fragment主动跟Activity通信
Activity实现特定接口,Fragment可以通过getActivity()获取到接口实例,这样就能操作Activity的方法了。
Activity跟Fragment通信
Activity中加载Fragment,自然拥有Fragment实例,可以执行Fragment 的方法。但是为了减少耦合,最好Fragment也实现特定接口,Activity中获取接口并执行其方法。
四、懒加载
/***
* 靠谱懒加载,不解释。
*/
public abstract class LazyFragment extends Fragment {
private static final long LOAD_START_DELAY_TIME = 150;//ms
protected boolean isDataInitiated;// 是否加载过数据
private View rootView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (rootView == null) {
rootView = createView(inflater, container, savedInstanceState);
}
return rootView;
}
protected abstract View createView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState);
/**
* 这里有几个选择,onViewCreated()、onActivityCreated() 或者 onStart()、onResume()。
* onActivityCreated() 是在Activity onCreate()方法返回后回调,在这里判断一次是否加载数据是有必要的。
* 1. onStart() 和 onResume()方法每次显示Fragment都会调用,太频繁,不可用。
* 2. onViewCreated() 这个方法是在View 布局加载完成时回调(加载View是在Activity的onCreate方法返回时才开始的),onActivityCreated()紧随其后,所以这两个方法都可以,效果没有大的差别。
*
* @param savedInstanceState
*/
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
prepareLoadData();
}
/**
* @param isVisibleToUser
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
prepareLoadData();
}
/**
* 判断懒加载条件
*/
public void prepareLoadData() {
if (!getUserVisibleHint() || isDataInitiated || rootView == null) {
return;
}
rootView.postDelayed(new Runnable() {
@Override
public void run() {
if (!getUserVisibleHint() || !isVisible() || isDataInitiated) {
return;
}
isDataInitiated = true;
loadData();
}
}, LOAD_START_DELAY_TIME);
}
/**
* 懒加载
*/
public abstract void loadData();
}