Android FragmentPagerAdapter动态增加

2017-11-25  本文已影响867人  LeonXtp

问题记录


背景

使用TabLayout+ViewPager+FragmentPagerAdapter实现经典的多Tab底部展示的布局结构。

需求

动态增删换Fragment

问题症状

删除异常,增加失败,替换失败

问题原因

问题主要还是在于FragmentPagerAdapter的缓存机制:

FragmentPagerAdapter会缓存所有的经过instantiate()之后的Fragment,保存在一个 ArrayList<Fragment>的数据结构中。在从缓存中取出来复用的时候,是根据Container的ID+ItemId共同判断的。这个containerId是哪个Container呢?就是ViewPager。所以,决定缓存取出策略的,基本就是itemId了。

由此也可以复现问题场景:

假设有4个Fragment,现在要将下表为1的那个Fragment删除。你直接从你的List<Fragment>中删除了它,然后当你点击第1个时,显示的却还是原来那个。
类似的,增加/替换,也是同样的症状。

解决办法

很简单,就是给每个Fragment都固定一个ID,在FragmentPagerAdaptergetItemId()方法中根据各种不同的展现情景返回,而不是默认的只返回position
在这里,最简单的方式就是返回你的List<Fragment>FragmenthashCode

这样就解决了增删替换的问题,如果要调换位置,也是可以的。

相关系统源码


READ THE FUCKING SOURCE CODE
FragmentPagerAdapter:

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ......
        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) {
            ......
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            ......
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
       ......
        return fragment;
    }
    public long getItemId(int position) {
        return position;
    }
    private static String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
    }

FragmentManager:

    public abstract Fragment findFragmentByTag(String tag);
    @Override
    public Fragment findFragmentByTag(String tag) {
        if (mAdded != null && tag != null) {
            // First look through added fragments.
            for (int i=mAdded.size()-1; i>=0; i--) {
                Fragment f = mAdded.get(i);
                if (f != null && tag.equals(f.mTag)) {
                    return f;
                }
            }
        }
        ......
        return null;
    }

其中mAdded声明是这样的:

    ArrayList<Fragment> mAdded;

好了,看了以上的源码足够解决问题了。

一篇不错的周期介绍

上一篇下一篇

猜你喜欢

热点阅读