Fragment小结

一、Fragment生命周期 Fragment小结
文章图片
Fragment生命周期
先找一张官方给定的生命周期。先简略的解释每个周期的作用:

  • onAttach(Context context) : Fragment与Activity完成绑定。传入的Context是宿主Activity,可以利用该上下文让Fragment和Activity通信。
  • onCreate( Bundle savedInstanceState) :初始化创建Fragment。参数savedInstanceState获取之前保存的值,同Activity中的一样。
  • onCreateView() :加载解析Fragment的布局,同Activity的onCreate类似。此回调方法不建议处理耗时的操作。
  • onViewCreated() :布局创建好了,就会回调这个方法。
  • onActivityCreated() :此时Activity的onCreate方法已经执行完成,在该方法内可以进行与Activity交互的UI操作,所以在该方法之前Activity的onCreate方法并未执行完成,如果提前进行交互操作,会引发空指针异常。
  • onStart() :Fragment由不可见状态变为可见状态。不处在前台,不可交互。
  • onResume() :Fragment处于活动状态,用户可与之交互。
  • onPause() :Fragment处于暂停状态,但依然可见,不处在前台,不可交互。
  • onStop() :Fragment不可见。
  • onDestroyView() :销毁与Fragment有关的视图,但未与Activity解除绑定。
  • onDestroy() :销毁Fragment。通常按Back键退出或者Fragment被回收时调用此方法。
  • onDetach() :与Activity解除绑定。Activity与该Fragment没啥关系了。
二、Activity关联Fragment方式 Activity中有以下几种关联fragment方式,每个方式触发Fragment的生命周期也是不一样的。
使用的是FragmentTransaction该类的操作方法。
方法 描述 生命周期
add(Fragment) 将Fragment添加到Activity onAttach->onCreateView()->onActivityCreated()->onStart()->onResume()
remove(Fragment) 从Activity中移除一个已将存在的Fragment onPause()->onStop()->onDestroyView() ->onDestroy()->onDetach()
replace(Fragment) 从Activity中移除当前的Fragment,并添加新的 当前的销毁,新的走新建的方法
show(Fragment) 配合hide使用,被hide的fragment再次show,会在Activity中显示 不走任何生命周期
hide(Fragment) hide()方法只是隐藏了fragment的view并没有将view从viewtree中删除,随后可用show()方法将view设置为显示 不走任何生命周期,可通过fragment.isHidden()判断是否隐藏了
attach(Fragment) 同detach配合使用,从内存中添加到Activity。fragment的状态还依然会保留着,再次调用onCreateView()来重绘视图 onCreateView()->onActivityCreated()->onStart()->onResume()
detach(Fragment) ,而detach则只是销毁其视图结构,将view从viewtree中删除,实例并不会被销毁 不走任何生命周期
三、Fragment的封装 正常的开发中,如ViewPager+Fragment开发需要一些特殊的回调:
  • Fragment第一次加载
  • Fragment切换时状态发生变化回调
    因为ViewPager是由预加载机制,所以只是用系统给定的生命周期,并不能很好的处理。
public abstract class BaseFragment extends Fragment {// 子类传入布局 protected abstract int getLayoutResource(); private boolean isFragmentVisible; private boolean isReuseView; private boolean isFirstVisible; private boolean hasCreateView; private View rootView; //setUserVisibleHint()在Fragment创建时会先被调用一次,传入isVisibleToUser = false //如果当前Fragment可见,那么setUserVisibleHint()会再次被调用一次,传入isVisibleToUser = true //如果Fragment从可见->不可见,那么setUserVisibleHint()也会被调用,传入isVisibleToUser = false //总结:setUserVisibleHint()除了Fragment的可见状态发生变化时会被回调外,在new Fragment()时也会被回调 //如果我们需要在 Fragment 可见与不可见时干点事,用这个的话就会有多余的回调了,那么就需要重新封装一个 @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); //setUserVisibleHint()有可能在fragment的生命周期外被调用 if (rootView == null) { return; } hasCreateView = true; if (isFirstVisible && isVisibleToUser) { onFragmentFirstVisible(); isFirstVisible = false; } if (isVisibleToUser) { onFragmentVisibleChange(true); isFragmentVisible = true; return; } if (isFragmentVisible) { isFragmentVisible = false; onFragmentVisibleChange(false); } }@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); initVariable(); }@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { rootView = inflater.inflate(getLayoutResource(),container,false); return rootView; }@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { //如果setUserVisibleHint()在rootView创建前调用时,那么 //就等到rootView创建完后才回调onFragmentVisibleChange(true) //保证onFragmentVisibleChange()的回调发生在rootView创建完成之后,以便支持ui操作 if (!hasCreateView) { if (getUserVisibleHint()) { if (isFirstVisible) { onFragmentFirstVisible(); isFirstVisible = false; } onFragmentVisibleChange(true); isFragmentVisible = true; } }super.onViewCreated(isReuseView ? rootView : view, savedInstanceState); }@Override public void onDestroyView() { super.onDestroyView(); }@Override public void onDestroy() { super.onDestroy(); initVariable(); }private void initVariable() { isFirstVisible = true; isFragmentVisible = false; rootView = null; isReuseView = true; }/** * 设置是否使用 view 的复用,默认开启 * view 的复用是指,ViewPager 在销毁和重建 Fragment 时会不断调用 onCreateView() -> onDestroyView() * 之间的生命函数,这样可能会出现重复创建 view 的情况,导致界面上显示多个相同的 Fragment * view 的复用其实就是指保存第一次创建的 view,后面再 onCreateView() 时直接返回第一次创建的 view * * @param isReuse */ protected void reuseView(boolean isReuse) { isReuseView = isReuse; }/** * 去除setUserVisibleHint()多余的回调场景,保证只有当fragment可见状态发生变化时才回调 * 回调时机在view创建完后,所以支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题 * * 可在该回调方法里进行一些ui显示与隐藏,比如加载框的显示和隐藏 * * @param isVisible true不可见 -> 可见 *false 可见-> 不可见 */ protected void onFragmentVisibleChange(boolean isVisible) { Log.e(getClass().getSimpleName(), "onFragmentVisibleChange: "+isVisible ); }/** * 在fragment首次可见时回调,可在这里进行加载数据,保证只在第一次打开Fragment时才会加载数据, * 这样就可以防止每次进入都重复加载数据 * 该方法会在 onFragmentVisibleChange() 之前调用,所以第一次打开时,可以用一个全局变量表示数据下载状态, * 然后在该方法内将状态设置为下载状态,接着去执行下载的任务 * 最后在 onFragmentVisibleChange() 里根据数据下载状态来控制下载进度ui控件的显示与隐藏 */ protected void onFragmentFirstVisible() { Log.e(getClass().getSimpleName(), "onFragmentFirstVisible: "); }}

四、结合ViewPager使用 ViewPager的给定的两个适配器一个是FragmentPagerAdapter,另一个是FragmentStatePagerAdapter。
(1)FragmentPagerAdapter FragmentPagerAdapter 是一个用于Fragment不多的情况下,因为他会将所有加载过的fragment全部保存在内存中。
源码来看,他就是使用的是attach 和 detach 方法,第一次的时候并没有一个fragment实例,先add一个,再次添加就使用attach ,它并不会整的销毁,当重新attach()时不会走Fragment中的onAttach()生命周期。
@Override public Object instantiateItem(ViewGroup container, int position) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); }final long itemId = getItemId(position); // Do we already have this fragment? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment); } else { fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); }return fragment; }@Override public void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object + " v=" + ((Fragment)object).getView()); mCurTransaction.detach((Fragment)object); }

(2)FragmentStatePagerAdapter 和FragmentPagerAdapter相反,该适配器适合用于大量的Fragment时,他会完全的销毁Fragment。
@Override public Object instantiateItem(ViewGroup container, int position) { // If we already have this item instantiated, there is nothing // to do.This can happen when we are restoring the entire pager // from its saved state, where the fragment manager has already // taken care of restoring the fragments we previously had instantiated. if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } }if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); }Fragment fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); if (mSavedState.size() > position) { Fragment.SavedState fss = mSavedState.get(position); if (fss != null) { fragment.setInitialSavedState(fss); } } while (mFragments.size() <= position) { mFragments.add(null); } fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); mFragments.set(position, fragment); mCurTransaction.add(container.getId(), fragment); return fragment; }@Override public void destroyItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment) object; if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment)object).getView()); while (mSavedState.size() <= position) { mSavedState.add(null); } mSavedState.set(position, fragment.isAdded() ? mFragmentManager.saveFragmentInstanceState(fragment) : null); mFragments.set(position, null); mCurTransaction.remove(fragment); }

核心源码就是两句:
  • 初始化:mCurTransaction.add(container.getId(), fragment);
  • 销毁 : mCurTransaction.remove(fragment);
    就是采用添加和移除,并没有缓存的功能。
参考 【Fragment小结】https://www.cnblogs.com/fajieyefu/p/6092465.html
https://www.cnblogs.com/dasusu/p/5926731.html
http://www.cnblogs.com/dasusu/p/6745032.html

    推荐阅读