ViewPager + Fragment组合实现局部刷新Fragment

在开发过程中,经常会用到ViewPager与Fragment实现多页面切换效果,有时,我们想要局部刷新某些Fragment,而其他Fragment保持状态不变,该如何做到呢?

先上代码!

/**
 * Created by HSH .
 */
public abstract class BaseFragmentPagerAdapter extends FragmentPagerAdapter {

    private FragmentManager mFragmentManager;

    //保存每个Fragment的Tag,刷新页面的依据
    protected SparseArray<String> tags = new SparseArray<>();

    public BaseFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
        mFragmentManager = fm;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        //得到缓存的fragment
        Fragment fragment = (Fragment) super.instantiateItem(container, position); 
        String tag = fragment.getTag();
        //保存每个Fragment的Tag
        tags.put(position, tag);
        return fragment;
    }

    //拿到指定位置的Fragment
    public Fragment getFragmentByPosition(int position) {
        return mFragmentManager.findFragmentByTag(tags.get(position));
    }

    public List<Fragment> getFragments(){
        return mFragmentManager.getFragments();
    }

    //刷新指定位置的Fragment
    public void notifyFragmentByPosition(int position) {
        tags.removeAt(position);
        notifyDataSetChanged();
    }

    @Override
    public int getItemPosition(Object object) {
        Fragment fragment = (Fragment) object;
        //如果Item对应的Tag存在,则不进行刷新
        if (tags.indexOfValue(fragment.getTag()) > -1) {
            return super.getItemPosition(object);
        }
        return POSITION_NONE;
    }
}

该类是本人最近在项目中使用的,只需继承该类即可。

/**
 * Created by HSH .
 */
public class CustomLrcPagerAdapter extends BaseFragmentPagerAdapter {
    private List<String> lrcs = new ArrayList<>();
    private MusicInfo info;

    public CustomLrcPagerAdapter(FragmentManager fm, MusicInfo info) {
        super(fm);
        this.info = info;
    }

    public void addDatas(List<String> lrcs) {
        this.lrcs.addAll(lrcs);
        notifyDataSetChanged();
    }

    @Override
    public Fragment getItem(int position) {
        return CustomLrcFragment.newInstance(info, lrcs.get(position), position);
    }

    //除了给定位置,其他位置的Fragment进行刷新
    public void notifyChangeWithoutPosition(int position) {
        String valueP = tags.valueAt(position);
        tags.clear();
        tags.put(position, valueP);
        notifyDataSetChanged();
    }


    @Override
    public int getCount() {
        return lrcs.size();
    }
}

刷新的核心原理很简单,相信看过源码的都会,在PagerAdapter中提供了一个方法:

/**
 * Called when the host view is attempting to determine if an item's position
 * has changed. Returns {@link #POSITION_UNCHANGED} if the position of the given
 * item has not changed or {@link #POSITION_NONE} if the item is no longer present
 * in the adapter.
 *
 * <p>The default implementation assumes that items will never
 * change position and always returns {@link #POSITION_UNCHANGED}.
 *
 * @param object Object representing an item, previously returned by a call to
 *               {@link #instantiateItem(View, int)}.
 * @return object's new position index from [0, {@link #getCount()}),
 *         {@link #POSITION_UNCHANGED} if the object's position has not changed,
 *         or {@link #POSITION_NONE} if the item is no longer present.
 */
public int getItemPosition(Object object) {
    return POSITION_UNCHANGED;
}

注释中已经说明了,当我们返回了POSITION_UNCHANGED,则表示页面数据不变,不进行更新;
返回POSITION_NONE,则表示页面不存在,需要进行更新。

因此,我重写了该方法:

    @Override
    public int getItemPosition(Object object) {
        Fragment fragment = (Fragment) object;
        //如果Item对应的Tag存在,则不进行刷新
        if (tags.indexOfValue(fragment.getTag()) > -1) {
            return super.getItemPosition(object);
        }
        return POSITION_NONE;
    }

hihi,是不是很简单!

在接触公司项目的过程中(本人新来的),发现公司项目中ViewPager+Fragment组合的使用方式存在问题,估计很多人也这么用过,就是定义一个集合用来缓存放到ViewPager中的Fragment,类似我们公司项目的这种做法:

/**
 * Created by Administrator on 2016/11/30.
 */
public class ViewPagerAdapter extends FragmentStatePagerAdapter {

    private List<Fragment> mList_Fragment = new ArrayList<>();
    private HashMap<Integer, Boolean> mList_Need_Update = new HashMap<>();
    private FragmentManager mFragmentManager;

    public ViewPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        mFragmentManager = fm;
        mList_Need_Update.clear();
        mList_Fragment.clear();
        if (fragments != null) {
            mList_Fragment.addAll(fragments);
        }
    }

//    @Override
//    public Object instantiateItem(ViewGroup container, int position) {
//        Fragment fragment = (Fragment) super.instantiateItem(container, position); //得到缓存的fragment
//
//        Boolean update = mList_Need_Update.get(position);
//        if (update != null && update) {
//            String fragmentTag = fragment.getTag(); //得到tag,这点很重要
//            FragmentTransaction ft = mFragmentManager.beginTransaction();
//            ft.remove(fragment); //移除旧的fragment
//            fragment = getItem(position); //换成新的fragment
//            ft.add(container.getId(), fragment, fragmentTag); //添加新fragment时必须用前面获得的tag,这点很重要
//            ft.attach(fragment);
//            ft.commit();
//            mList_Need_Update.put(position, false); //清除更新标记(只有重新启动的时候需要去创建新的fragment对象),防止正常情况下频繁创建对象
//        }
//
//        return fragment;
//    }

    public List<Fragment> getListFragment(){
        return mList_Fragment;
    }

    public void setListFragment(List<Fragment> list_Fragment) {
//        if(list_Fragment != null){
//            FragmentTransaction ft = mFragmentManager.beginTransaction();
//            for (int i = 0; i < mList_Fragment.size(); i++) {
//                Fragment fragment = (Fragment) mList_Fragment.get(i);
//                ft.remove(fragment);
//            }
//            ft.commit();
//            ft = null;
//            mFragmentManager.executePendingTransactions();
//        }
        mList_Need_Update.clear();
        this.mList_Fragment.clear();
        if (list_Fragment != null) {
            this.mList_Fragment.addAll(list_Fragment);
        }
        notifyDataSetChanged();
    }

    public void setListNeedUpdate(List<Fragment> fragments) {
        mList_Fragment.clear();
        if (fragments != null) {
            mList_Fragment.addAll(fragments);
        }
        mList_Need_Update.clear();
        for (int i = 0; i < mList_Fragment.size(); i++) {
            mList_Need_Update.put(i, true);
        }
    }

    @Override
    public Fragment getItem(int position) {
        if(mList_Fragment.size() < position){
            return null;
        }
        return mList_Fragment.get(position);
    }


    @Override
    public int getCount() {
        return mList_Fragment.size();
    }

    @Override
    public int getItemPosition(Object object) {
        return PagerAdapter.POSITION_NONE;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        try {
            super.restoreState(state, loader);
        } catch (Exception e) {

        }

    }
}

这种做法看似方便我们操作ViewPager中的Fragment,但是存在一个很致命的问题。

某些情况下,我们在从其他页面回退到ViewPager,在进行Fragment数据更新时,会发现居然没有效果(或者效果很诡异,例如会出现多次调用的情况)。

这种情况其实就是Fragment进行了热启动。(我的说法不知是否准确,指的就是内存不足时,页面被销毁了并调用了onSaveInstanceState方法,在重新回到页面时,我们可以从Bundle savedInstanceState中拿到之前缓存的数据。)

由于FragmentPagerAdapter中的FragmentManager已经帮我们缓存了所有Fragment,并且在数据恢复时,也自动帮我们进行恢复处理。
所以,个人猜测 (未经源码验证的!) ,在FragmentManager进行数据恢复时,如果我们本地通过集合缓存了一份Fragment,则这份Fragment与FragmentManager进行数据恢复后的Fragment是不同的!

我个人的做法是,每次需要操作ViewPager中的Fragment时,都从FragmentManager中拿:

    //拿到指定位置的Fragment
    public Fragment getFragmentByPosition(int position) {
        return mFragmentManager.findFragmentByTag(tags.get(position));
    }

    public List<Fragment> getFragments(){
        return mFragmentManager.getFragments();
    }

东西很简单,但相信对新手还是有点用处的,至少本人当初在这个问题上是被整得欲仙欲死 /(ㄒoㄒ)/~~

说明一下,以上只是个人猜测,本人懒,没去看源码,如果有哪位看官知道原理的,或者本人理解存在错误的,还请在评论中进行指正!谢谢!

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值