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

来源:互联网 发布:石家庄手机数据恢复 编辑:程序博客网 时间:2024/06/01 09:31

一个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>>>>>213155854307-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?

阅读全文
0 0
原创粉丝点击