美文网首页
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