viewpager布局复用中FragmentPagerAdapter的坑,源码分析,控件id的一些思考
一个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?
下一篇: 【Leetcode】Two Sum题解