ViewPager2配合TabLayout使用时重写适配器方法后功能异常问题
问题根源
你的重写逻辑完全违背了FragmentStateAdapter对getItemId、containsItem的设计要求,具体问题有2个:
getItemId要求为固定位置返回唯一、稳定、不随Fragment实例变化的ID,你用Fragment实例的hashCode作为返回值,且自定义拼接的FragmentTag不符合FragmentStateAdapter的生成规则,会出现同一个位置在Fragment创建前后返回不同ID的情况,导致ViewPager2无法正确匹配位置和Fragment实例。containsItem的校验逻辑和getItemId的返回值不匹配,ViewPager2校验ID时反复判定ID不存在,触发循环校验,直接导致死循环和Fragment生命周期错乱,最终出现菜单残留的问题。
解决步骤
第一步:正确重写两个方法
你只有2个固定的Tab,直接为每个位置返回固定的ID即可,不需要操作FragmentManager或者依赖Fragment实例:
@Override public long getItemId(int position) { // 固定位置返回固定ID,不受Fragment实例状态影响 return position; } @Override public boolean containsItem(long itemId) { // 直接校验ID是否在有效Tab范围内即可 return itemId >= 0 && itemId < getItemCount(); }
第二步:正确实现Fragment数据刷新
你不需要完全依赖notifyItemChanged触发刷新,在Activity的onResume中拿到已创建的Fragment实例,直接调用实例的更新方法更可靠:
// Activity onResume中拿到新数据后执行 int viewPagerId = viewPager.getId(); // 首页Fragment,tag格式是FragmentStateAdapter默认生成的规则:f + ViewPagerID + : + 位置 Fragment homeFrag = getSupportFragmentManager().findFragmentByTag("f" + viewPagerId + ":0"); if (homeFrag instanceof BusinessCard) { ((BusinessCard) homeFrag).updateData(myContact); } // 联系人Fragment Fragment contactFrag = getSupportFragmentManager().findFragmentByTag("f" + viewPagerId + ":1"); if (contactFrag instanceof ContactList) { ((ContactList) contactFrag).updateData(listContacts); } // 如果需要强制刷新UI再调用notifyItemChanged,此时已经不会出现ID错乱问题 adapter.notifyItemChanged(0); adapter.notifyItemChanged(1);
第三步:修复菜单残留问题
每个Fragment中要确保:
- 在
onCreate/onCreateView中调用setHasOptionsMenu(true) - 重写
onPrepareOptionsMenu,在该方法中根据当前Fragment的需求控制菜单项的显示隐藏 - 每次Fragment切换到前台时调用
requireActivity().invalidateOptionsMenu()触发菜单重绘
内容的提问来源于stack exchange,提问作者RacmanT
相关产品推荐
相关产品推荐

