美文网首页
fragment实现Tab切换效果

fragment实现Tab切换效果

作者: 刘孙猫咪 | 来源:发表于2017-07-03 11:16 被阅读0次

    上篇播客从源码和注释说了下fragment的生命周期,这里还是接着说fragment并实现Tab切换效果,先看下运行的效果;

    GIF.gif

    实现这个效果其实很简单,通过getSupportFragmentManager()返回一个FragmentManager对象,调用FragmentManager中的beginTransaction()方法返回一个FragmentTransaction对象,调用FragmentTransaction中的add方法将fragment添加到FragmentActivity中,调用FragmentTransaction中的commit()方法就实现了;

        /**
         * 添加Fragment
         * @param fragment
         */
        public void add(Fragment fragment){
            FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
            //添加fragment
            fragmentTransaction.add(mComtainViewId, fragment);
            fragmentTransaction.commit();
        }
    

    在进行fragment切换的时候调用FragmentTransaction 中的replace()方法和commit()方法就可以了;

        /**
         * 替换Fragment
         * @param fragment
         */
        public void replace(Fragment fragment){
            FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
            //添加fragment
            fragmentTransaction.replace(mComtainViewId, fragment);
            fragmentTransaction.commit();
        }
    

    就这些代码就实现了,接下来看看我们在调用这些方法的时候,源码里面干了哪些事情;
    我们在调用getSupportFragmentManager获取FragmentManager对象的时候,涉及到了FragmentController、FragmentManagerImpl、FragmentHostCallback这些类;
    FragmentManagerImpl继承自FragmentManager;
    一开始调用的是FragmentActivity这个方法,

    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
      /**
         * Return the FragmentManager for interacting with fragments associated
         * with this activity.
         */
        public FragmentManager getSupportFragmentManager() {
            return mFragments.getSupportFragmentManager();
        }
    

    这里的mFragments就是FragmentController集合,接着又调用了FragmentController中的

      /**
         * Returns a {@link FragmentManager} for this controller.
         */
        public FragmentManager getSupportFragmentManager() {
            return mHost.getFragmentManagerImpl();
        }
    

    这里的mHost就是FragmentHostCallback类,最后调用了FragmentHostCallback中的

    FragmentManagerImpl getFragmentManagerImpl() {
            return mFragmentManager;
        }
    

    返回一个FragmentManagerImpl对象;

    FragmentManager在调用beginTransaction()方法应该是调用了子类FragmentManagerImpl中的beginTransaction()方法,返回一个BackStackRecord对象,而BackStackRecord是FragmentTransaction的子类,

      @Override
        public FragmentTransaction beginTransaction() {
            return new BackStackRecord(this);
        }
    

    拿到FragmentTransaction对象后,调用add等方法时,一看源码FragmentTransaction中的这些方法都是抽象方法,都是在BackStackRecord子类中做了相应的动作;

        @Override
        public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
            doAddOp(containerViewId, fragment, tag, OP_ADD);
            return this;
        }
    

    调用add最终是调用了doAddOp将fragment添加进来;

      private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
            final Class fragmentClass = fragment.getClass();
            final int modifiers = fragmentClass.getModifiers();
            if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
                    || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
                throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                        + " must be a public static class to be  properly recreated from"
                        + " instance state.");
            }
    
            fragment.mFragmentManager = mManager;
    
            if (tag != null) {
                if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                    throw new IllegalStateException("Can't change tag of fragment "
                            + fragment + ": was " + fragment.mTag
                            + " now " + tag);
                }
                fragment.mTag = tag;
            }
    
            if (containerViewId != 0) {
                if (containerViewId == View.NO_ID) {
                    throw new IllegalArgumentException("Can't add fragment "
                            + fragment + " with tag " + tag + " to container view with no id");
                }
                if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                    throw new IllegalStateException("Can't change container ID of fragment "
                            + fragment + ": was " + fragment.mFragmentId
                            + " now " + containerViewId);
                }
                fragment.mContainerId = fragment.mFragmentId = containerViewId;
            }
    
            Op op = new Op();
            op.cmd = opcmd;
            op.fragment = fragment;
            addOp(op);
        }
    

    调用commit()方法提交事务的时候,调用的是FragmentManager的scheduleCommit()方法,再通过FragmentHostCallBack中的getHandler返回一个Handler对象去执行,

        /**
         * Schedules the execution when one hasn't been scheduled already. This should happen
         * the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
         * a postponed transaction has been started with
         * {@link Fragment#startPostponedEnterTransition()}
         */
        private void scheduleCommit() {
            synchronized (this) {
                boolean postponeReady =
                        mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
                boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
                if (postponeReady || pendingReady) {
                    mHost.getHandler().removeCallbacks(mExecCommit);
                    mHost.getHandler().post(mExecCommit);
                }
            }
        }
    

    这里的mHost是FragmentHostCallBack对象,在fragment切换的时候调用replace就可以了,其实还是调用了添加的方法来实现的,

        @Override
        public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
            if (containerViewId == 0) {
                throw new IllegalArgumentException("Must use non-zero containerViewId");
            }
    
            doAddOp(containerViewId, fragment, tag, OP_REPLACE);
            return this;
        }
    

    细心下就会发现,调用replace方法后,再切换回来的时候,页面又重新加载一遍了,

    QQ截图20170702220000.jpg

    是不是感觉对于不想重新加载页面来说是不是太坑了,其实如果不想重新加载页面的话可以调用FragmentTransaction中的hide和show方法就可以了,

        /**
         * 切换显示Fragment
         * @param fragment
         */
        public void switchFragment(Fragment fragment){
            FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
            //获取当前所有的fragment
            List<Fragment> childFragments = mFragmentManager.getFragments();
            //先隐藏所有的fragment
            for(Fragment childFragment:childFragments){
                fragmentTransaction.hide(childFragment);
            }
            //没有的话就添加,有就显示
            if(!childFragments.contains(fragment)){
                //添加
                fragmentTransaction.add(mComtainViewId,fragment);
            }else{
                fragmentTransaction.show(fragment);
            }
            fragmentTransaction.commit();
        }
    

    想重新加载页面的话就调用replace,不想的话就调用hide和show就可以了,最后说下相关类的关系及包含什么东西;
    FragmentManagerImpl是FragmentManager的子类,提供了beginTransaction、getFragment、getFragments、putFragment等方法,
    BackStackRecord是FragmentTransaction的子类,提供了add、replace、hide、show、remove等方法,
    FragmentController提供了getSupportFragmentManager等方法
    FragmentHostCallback类,其实fragment中很多方法的调用都走到这里,就那getActivity来说,

        /**
         * Return the {@link FragmentActivity} this fragment is currently associated with.
         * May return {@code null} if the fragment is associated with a {@link Context}
         * instead.
         */
        final public FragmentActivity getActivity() {
            return mHost == null ? null : (FragmentActivity) mHost.getActivity();
        }
    

    这里的mHost 其实就FragmentHostCallback,调的就是FragmentHostCallback中的getActivity返回一个activity对象,

     Activity getActivity() {
            return mActivity;
        }
    

    getContext()、startActivity()、startActivityForResult()等都是这样调用的;
    startActivity通过调用FragmentHostCallback中的onStartActivityFromFragment方法,再由Context调用startActivity来实现的;

    fragment中startActivity

        /**
         * Call {@link Activity#startActivity(Intent, Bundle)} from the fragment's
         * containing Activity.
         */
        public void startActivity(Intent intent, @Nullable Bundle options) {
            if (mHost == null) {
                throw new IllegalStateException("Fragment " + this + " not attached to Activity");
            }
            mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options);
        }
    

    FragmentHostCallback中onStartActivityFromFragment

        /**
         * Starts a new {@link Activity} from the given fragment.
         * See {@link FragmentActivity#startActivityForResult(Intent, int, Bundle)}.
         */
        public void onStartActivityFromFragment(
                Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
            if (requestCode != -1) {
                throw new IllegalStateException(
                        "Starting activity with a requestCode requires a FragmentActivity host");
            }
            mContext.startActivity(intent);
        }
    

    其他的一些东西有时间可以慢慢研究,上面这些如有写的不对欢迎交流。
    源码地址:http://pan.baidu.com/s/1miDkUEG

    相关文章

      网友评论

          本文标题:fragment实现Tab切换效果

          本文链接:https://www.haomeiwen.com/subject/momocxtx.html