欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

viewpager布局复用中FragmentPagerAdapter的坑,源码分析,控件id的一些思考

程序员文章站 2022-07-14 18:06:27
...

一个fragment的布局复用,里面是tablayout+viewpager,viewpager加载不同adapter,adapter继承FragmentPageAdapter。运行后有问题,先初始化的fragment正常显示,后加载的fragment里的viewpager全部是空白,这就很尴尬了,第一反应是fragment没add进FragmentManager,因为在同一个activity里,所以只有一个FragmentManager,debug一下。

List<Fragment> fragments = getSupportFragmentManager().getFragments();

果然第二个viewpager里的fragment一个都没add,继续查问题。FragmentPageAdapter是通过getItem(position)获取fragment对象的,并且已初始化过得fragment不会再次调用,会从FragmentManager中取出来,debug发现第二个getItem并未被调用,问题很明显,FragmentPageAdapter认为对应position的fragment已经初始化过了,不重新调用,好吧,查源码吧。

 @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        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) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }
private static String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
    }

FragmentPageAdapter 继承自PageAdapter,实现了instantiateItem方法,返回fragment对象。通过findFragmentByTag查找fragment,如果存在就不调用getItem方法,于是关键就在于container.getId(),这个container其实就是我们的viewpager,打印viewpager.getId()

07-24 21:55:21.605 7796-7796/cf.movie.slmovie E/viewPage: movie>>>>>2131558543
07-24 21:55:21.640 7796-7796/cf.movie.slmovie E/viewPage: tv>>>>>2131558543

果然没错,id是一样的,所以FragmentPageAdapter 不会重新调用getItem,这还没完,怎么办呢,布局copy一份,单独用,还是没用。好吧,viewpage的id改了一下,ok。
控件id的一些思考:
我们都知道每个view,layout系统都会分配一个id,保存在R文件里,一一对应,使用的时候view.findViewById()去找对应的组件,这个id的生成机制没找到。但是我们自己给view设置了一个id,项目庞大的时候,我们设置的这个id可能会有重复的,但是我们同一个view,同一个layout中的id不会重复,所以不用担心重名的问题。
查找R文件发现同名的view只会生成一个id。

public static final int viewPage=0x7f0d0090;

回到本问题中,fragment添加的tag代码如下

"android:switcher:" + viewId + ":" + id;

viewId不同,才能标识不同的fragment,但是即使加上view.getParent()的id,依然无法保证他的唯一性,因为嵌套层数可能非常多。
不知道这个类设计的时候出于什么考虑设计成这样,应该留一个方法去处理设置tag比较好一点。
于是尝试重写instantiateItem方法,添加标识

 @Override
    public Object instantiateItem(ViewGroup container, int position) {
        FragmentTransaction mCurTransaction = fm.beginTransaction();

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String name = makeFragmentName(which.toString(), container.getId(), itemId);
        Fragment fragment = fm.findFragmentByTag(name);
        if (fragment != null) {
            mCurTransaction.attach(fragment).commitAllowingStateLoss();
        } else {
            fragment = getItem(position);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(which.toString(), container.getId(), itemId)).commitAllowingStateLoss();
        }
        if (fragment != getItem(position - 1)) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }
        String tag = fragment.getTag();
        LogUtils.e("viewPager", "tag>>>>>" + tag);
        return fragment;
    }

    private static String makeFragmentName(String className, int viewId, long id) {
        return className + "android:switcher:" + viewId + ":" + id;
    }

事实证明,没用,但是FragmentManager中有正确的fragment,但是就是不行,症状一样,继续翻源码,只找到FragmentPageAdapter 中的一段注释

When using FragmentPagerAdapter the host ViewPager must have a valid ID set.

可能对valid ID理解不同, 相同的ID不是有效的ID吗,一定要unique?