快速实现Fragment切换功能

1. 前言

  • 一个app首页通常是使用Activity + Fragment的形式展示,控制Fragment的显示和隐藏基本有两种实现方法

    1. ViewPager , 比如微信 , 优势是手势操作更加方便,官方提供了FragmentPagerAdapter可以很方便帮助我们实现数据加载(Fragment要使用懒加载的方式,避免浪费资源),劣势就是当你的第一个Fragment中已经使用了ViewPager,两层套一起事件会冲突,而且操作也不友好啦。
    2. FragmentManager , 比如头条,针对使用ViewPager组合Fragment的问题,使用FragmentManager控制Fragment的显示和隐藏,不需要考虑懒加载的问题,不过不能支持滑动啦。
  • 既然是经常使用的功能,自然要分离出来,避免下次做重复的工作,本文主要介绍这个功能的实现。

2. 最初的方案

  • 刚开始的方案是定义了一个Activity的基类MultiFragmentActivity,在基类中实现切换Fragment的逻辑,想要实现该功能的Activity继承基类就可以实现切换功能。MultiFragmentActivity.java源代码,这种实现方式使用起来要更加简单,但是有个问题,整个项目通常会实现同一个基类来完成类似或者相同的功能,当然我们可以将MultiFragmentActivity继承自项目的基类,然后再继承MultiFragmentActivity,但是我的想法是能把这个功能放到Library中,方便以后使用,那就面临一个问题就是不能采用继承的方式。

  • 少用继承,多用组合,继承造成的问题就是耦合性太高,不方便分离功能,所以决定使用组合的方法重新设计,定义帮助类FragmentHelper,Activity可以持有FragmentHelper来完成Fragment的切换,具体的切换逻辑由FragmentHelper来处理。

3. 定义接口

  • 首先定义FragmentOperator接口,借助该接口FragmentHelper连接持有它的Activity

    interface FragmentOperator {
        /**
         * 获取放置fragment的控件id
         *
         * @return id
         */
        int getFragmentContainerId();

        /**
         * 构建fragment
         *
         * @param showItem 将要展示的fragment pos
         * @return fragment
         */
        Fragment makeFragment(int showItem);

        /**
         * 进行转换之前做操作,动画之类的
         *
         * @return FragmentTransaction
         */
        void beginTransaction(FragmentTransaction transaction);

        /**
         * 同步选中之后的显示状态
         *
         * @param selectImage 被选中的item
         */
        void syncSelectState(int selectImage);

        /**
         * 当点击显示同一个
         *
         * @param showItem 显示的item
         * @return 返回false表示忽略此次点击的切换
         */
        boolean whenShowSameFragment(int showItem);

        /**
         * 当点击显示的不是同一个
         *
         * @param showItem 显示的item
         * @return 返回false表示忽略此次点击的切换
         */
        boolean whenShowNotSameFragment(int showItem);
    }

     // 一个简单封装
    public static abstract class SimpleFragmentOperator implements FragmentOperator {
        @Override
        public boolean whenShowNotSameFragment(int showItem) {
            return true;
        }

        @Override
        public boolean whenShowSameFragment(int showItem) {
            return false;
        }

        @Override
        public void syncSelectState(int selectImage) {

        }

        @Override
        public void beginTransaction(FragmentTransaction transaction) {

        }
    }

4. 核心方法

  • 使用tag作为标记添加fragment,避免重复创建
    private static final String FRAGMENT_ATG = "FragmentHelper";
    private static final String ITEM_HIDE = "mHideItem";
    private static final String ITEM_SHOW = "mShowItem";

    private FragmentOperator operator;
    private Fragment mCurrentFragment;
    private FragmentManager mFragmentManager;
    private int mShowItem, mHideItem;
    private int mExactlyItem = 0;



    /**
     * 隐藏当前显示的fragment,显示将要显示的fragment
     *
     * @param hideItem   需要隐藏的fragment
     * @param showItem   需要显示的fragment
     * @param isOnCreate 是否是第一次从OnCreate中启动,点击都是false
     */
    private void performSelectItem(int hideItem, int showItem, boolean isOnCreate) {
        // 获得将要显示页的tag
        String currentTag = getFragmentTag(hideItem);
        // 隐藏当前的的fragment
        FragmentTransaction transaction = mFragmentManager.beginTransaction();
        operator.beginTransaction(transaction);

        // 第一次创建,一个都没有,不需要隐藏,直接显示
        if (mFragmentManager.getFragments() == null) {
            mShowItem = showItem;
            mExactlyItem = showItem;
            mCurrentFragment = operator.makeFragment(showItem);
            transaction.add(operator.getFragmentContainerId(), mCurrentFragment, getFragmentTag(showItem))
                    .show(mCurrentFragment);
        } else {
            // 优化,如果被杀后再进来,全部的fragment都会被呈现显示状态,所以都隐藏一遍
            if (isOnCreate && mFragmentManager.getFragments() != null) {
                for (Fragment fragment : mFragmentManager.getFragments()) {
                    transaction.hide(fragment);
                }
            } else {
                // 正常按钮点击进入,隐藏上一个即可
                Fragment lastFragment = mFragmentManager.findFragmentByTag(currentTag);
                if (lastFragment != null) {
                    transaction.hide(lastFragment);
                }
            }


            // 获得将要显示页的tag
            String toTag = getFragmentTag(showItem);
            // find要显示的Fragment
            mCurrentFragment = mFragmentManager.findFragmentByTag(toTag);
            if (mCurrentFragment != null) {
                // 已经存在则显示
                transaction.show(mCurrentFragment);
            } else {
                // 不存在则添加新的fragment
                mCurrentFragment = operator.makeFragment(showItem);
                if (mCurrentFragment != null) {
                    transaction.add(operator.getFragmentContainerId(), mCurrentFragment, toTag);
                }
            }
        }
        // 同步状态
        operator.syncSelectState(showItem);
        // 保存当前显示fragment的item
        mHideItem = hideItem;
        mShowItem = showItem;
        transaction.commitAllowingStateLoss();
    }

5. 在Activty显示某个Fragment

  • 在Activity中使用时只需要调用showFragment方法即可
    /**
     * 显示某个fragment
     *
     * @param showItem 显示的item
     */
    public void showFragment(int showItem) {
        showFragment(showItem, false);
    }


    /**
     * 选中某一个fragment
     *
     * @param showItem   显示的item
     * @param isOnCreate 是否是第一次创
     */
    private void showFragment(int showItem, boolean isOnCreate) {
        if (showItem == mShowItem) {
            if (operator.whenShowSameFragment(showItem)) {
                performSelectItem(mExactlyItem, showItem, isOnCreate);
                mExactlyItem = showItem;
            }
        } else {
            performSelectItem(mExactlyItem, showItem, isOnCreate);
            mExactlyItem = showItem;
        }
    }

6. 优化

  • 当Activity被回收时,记录上次的状态
    public void restoreFragmentHelper(Bundle save) {
        if (save != null) {
            mHideItem = save.getInt(ITEM_HIDE, 0);
            mShowItem = save.getInt(ITEM_SHOW, 0);
        }
        performSelectItem(mHideItem, mShowItem, true);
    }


    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(ITEM_HIDE, mHideItem);
        outState.putInt(ITEM_SHOW, mShowItem);
    }

7. 使用

public class HomePageActivity extends BaseReaperActivity {  

    private FragmentHelper fragmentHelper;

    private FragmentHelper.SimpleFragmentOperator operator = new FragmentHelper.SimpleFragmentOperator() {
        @Override
        public int getFragmentContainerId() {
            return R.id.home_container;
        }

        @Override
        public Fragment makeFragment(int showItem) {
            Fragment fragment = null;
            switch (showItem) {
                case 0:
                    fragment = HomeVideoFunFragment.newInst();
                    break;
                case 1:
                    fragment = HomeFunnyFragment.newInst();
                    break;
                case 2:
                    fragment = HomeBeautyFragment.newInst();
                    break;
                case 3:
                    fragment = HomeMineFragment.newInst();
                    break;
            }
            return fragment;
        }

        @Override
        public void beginTransaction(FragmentTransaction transaction) {
            super.beginTransaction(transaction);
            Logger.e("beginTransaction");
        }

        @Override
        public void syncSelectState(int selectImage) {
            for (int i = 0; i < mBotTabsTv.size(); i++) {
                mBotTabsTv.get(i).setSelected(selectImage == i);
            }
        }

        @Override
        public boolean whenShowNotSameFragment(int showItem) {
            JCVideoPlayer.releaseAllVideos();
            return super.whenShowNotSameFragment(showItem);
        }
    };


    @Override
    public void onInitViews(View view, Bundle saveData) {
        super.onInitViews(view, saveData);
        fragmentHelper = new FragmentHelper(getSupportFragmentManager(), operator);
        fragmentHelper.showFragment(2);
    }

    @OnClick({R.id.home_recommend, R.id.home_album, R.id.home_search, R.id.home_mine})
    public void click(View v) {
        int tag = Integer.parseInt(v.getTag().toString());
        fragmentHelper.showFragment(tag);
    }

    @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
        fragmentHelper.onSaveInstanceState(outState);
    }

    @Override
    public void onInitViews(View view, Bundle saveData) {
        super.onInitViews(view, saveData);
        fragmentHelper = new FragmentHelper(getSupportFragmentManager(), operator);
        fragmentHelper.showFragment(2);
    }
}

8. 源代码


/**
 * Project  : CommonLib
 * Package  : com.march.lib.core.common
 * CreateAt : 2016/11/5
 * Describe : 实现Fragment切换
 *
 * @author chendong
 */
public class FragmentHelper {

    interface FragmentOperator {
        /**
         * 获取放置fragment的控件id
         *
         * @return id
         */
        int getFragmentContainerId();

        /**
         * 构建fragment
         *
         * @param showItem 将要展示的fragment pos
         * @return fragment
         */
        Fragment makeFragment(int showItem);

        /**
         * 进行转换之前做操作,动画之类的
         *
         * @return FragmentTransaction
         */
        void beginTransaction(FragmentTransaction transaction);

        /**
         * 同步选中之后的显示状态
         *
         * @param selectImage 被选中的item
         */
        void syncSelectState(int selectImage);

        /**
         * 当点击显示同一个
         *
         * @param showItem 显示的item
         * @return 返回false表示忽略此次点击的切换
         */
        boolean whenShowSameFragment(int showItem);

        /**
         * 当点击显示的不是同一个
         *
         * @param showItem 显示的item
         * @return 返回false表示忽略此次点击的切换
         */
        boolean whenShowNotSameFragment(int showItem);
    }

    public static abstract class SimpleFragmentOperator implements FragmentOperator {
        @Override
        public boolean whenShowNotSameFragment(int showItem) {
            return true;
        }

        @Override
        public boolean whenShowSameFragment(int showItem) {
            return false;
        }

        @Override
        public void syncSelectState(int selectImage) {

        }

        @Override
        public void beginTransaction(FragmentTransaction transaction) {

        }
    }

    private static final String FRAGMENT_ATG = "FragmentHelper";
    private static final String ITEM_HIDE = "mHideItem";
    private static final String ITEM_SHOW = "mShowItem";

    private FragmentOperator operator;
    private Fragment mCurrentFragment;
    private FragmentManager mFragmentManager;
    private int mShowItem, mHideItem;
    private int mExactlyItem = 0;


    public void restoreFragmentHelper(Bundle save) {
        if (save != null) {
            mHideItem = save.getInt(ITEM_HIDE, 0);
            mShowItem = save.getInt(ITEM_SHOW, 0);
        }
        performSelectItem(mHideItem, mShowItem, true);
    }


    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(ITEM_HIDE, mHideItem);
        outState.putInt(ITEM_SHOW, mShowItem);
    }


    public FragmentHelper(FragmentManager mFragmentManager, FragmentOperator operator) {
        this.mFragmentManager = mFragmentManager;
        this.mFragmentManager = mFragmentManager;
        this.operator = operator;
    }


    /**
     * 显示某个fragment
     *
     * @param showItem 显示的item
     */
    public void showFragment(int showItem) {
        showFragment(showItem, false);
    }


    /**
     * 选中某一个fragment,处理重复点击
     *
     * @param showItem   显示的item
     * @param isOnCreate 是否是第一次创
     */
    private void showFragment(int showItem, boolean isOnCreate) {
        if (showItem == mShowItem) {
            if (operator.whenShowSameFragment(showItem)) {
                performSelectItem(mExactlyItem, showItem, isOnCreate);
                mExactlyItem = showItem;
            }
        } else {
            performSelectItem(mExactlyItem, showItem, isOnCreate);
            mExactlyItem = showItem;
        }
    }

    /**
     * 获取当前处于活动状态的fragment'
     *
     * @return fragment
     */
    public Fragment getCurrentFragment() {
        return mCurrentFragment;
    }

    /**
     * 隐藏当前显示的fragment,显示将要显示的fragment
     *
     * @param hideItem   需要隐藏的fragment
     * @param showItem   需要显示的fragment
     * @param isOnCreate 是否是第一次从OnCreate中启动,点击都是false
     */
    private void performSelectItem(int hideItem, int showItem, boolean isOnCreate) {
        // 获得将要显示页的tag
        String currentTag = getFragmentTag(hideItem);
        // 隐藏当前的的fragment
        FragmentTransaction transaction = mFragmentManager.beginTransaction();
        operator.beginTransaction(transaction);

        // 第一次创建,一个都没有,不需要隐藏,直接显示
        if (mFragmentManager.getFragments() == null) {
            mShowItem = showItem;
            mExactlyItem = showItem;
            mCurrentFragment = operator.makeFragment(showItem);
            transaction.add(operator.getFragmentContainerId(), mCurrentFragment, getFragmentTag(showItem))
                    .show(mCurrentFragment);
        } else {
            // 优化,如果被杀后再进来,全部的fragment都会被呈现显示状态,所以都隐藏一遍
            if (isOnCreate && mFragmentManager.getFragments() != null) {
                for (Fragment fragment : mFragmentManager.getFragments()) {
                    transaction.hide(fragment);
                }
            } else {
                // 正常按钮点击进入,隐藏上一个即可
                Fragment lastFragment = mFragmentManager.findFragmentByTag(currentTag);
                if (lastFragment != null) {
                    transaction.hide(lastFragment);
                }
            }


            // 获得将要显示页的tag
            String toTag = getFragmentTag(showItem);
            // find要显示的Fragment
            mCurrentFragment = mFragmentManager.findFragmentByTag(toTag);
            if (mCurrentFragment != null) {
                // 已经存在则显示
                transaction.show(mCurrentFragment);
            } else {
                // 不存在则添加新的fragment
                mCurrentFragment = operator.makeFragment(showItem);
                if (mCurrentFragment != null) {
                    transaction.add(operator.getFragmentContainerId(), mCurrentFragment, toTag);
                }
            }
        }
        // 同步状态
        operator.syncSelectState(showItem);
        // 保存当前显示fragment的item
        mHideItem = hideItem;
        mShowItem = showItem;
        transaction.commitAllowingStateLoss();
    }


    private String getFragmentTag(int item) {
        return FRAGMENT_ATG + item;
    }
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值