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);
    就是采用添加和移除,并没有缓存的功能。

参考

https://www.cnblogs.com/fajieyefu/p/6092465.html
https://www.cnblogs.com/dasusu/p/5926731.html
http://www.cnblogs.com/dasusu/p/6745032.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容