集成Jetpack Navigation后,如何获取ViewPager2当前页面Fragment实例?
问题分析
引入Jetpack Navigation后,ViewPager2关联的Fragment管理逻辑发生了两个关键变化:
- Navigation的NavHostFragment会接管导航栈内Fragment的生命周期管理,导致HomePager的
childFragmentManager不再直接持有ViewPager2的页面Fragment(你观察到childFragmentManager.fragments为空就是这个原因)。 - ViewPager2的
FragmentStateAdapter默认生成的Fragment标签格式是"f${viewPagerId}:${position}",而非你之前手动使用的"f${position}",直接用旧标签查找自然返回null。
解决方案
方案1:使用ViewModel传递焦点事件(推荐,符合Jetpack架构)
避免直接获取Fragment实例,通过共享ViewModel传递焦点指令,让HomeFragment自行处理焦点设置:
1.1 创建共享ViewModel
class HomePagerViewModel : ViewModel() { // 存储目标页面位置和列表项位置 val focusEvent = MutableLiveData<Pair<Int, Int>>() }
1.2 在HomePager中发送焦点事件
// 初始化ViewModel val viewModel = ViewModelProvider(this)[HomePagerViewModel::class.java] val psLis: (Char, Int) -> Unit = { direction, pos -> val curItem = viewPager.currentItem val TAG = "psLis" Log.d(TAG, "Page-switch listener initiated.") if (direction == 'l' && curItem > 0) { val targetPage = curItem - 1 viewPager.currentItem = targetPage // 发送焦点事件:目标页面、目标列表项位置 viewModel.focusEvent.value = Pair(targetPage, pos + 3) } else if (direction == 'r' && curItem < (NUM_PAGES - 1)) { val targetPage = curItem + 1 viewPager.currentItem = targetPage viewModel.focusEvent.value = Pair(targetPage, pos - 3) } }
1.3 在HomeFragment中监听焦点事件
// 获取父Fragment的ViewModel(HomePager的ViewModel) val viewModel = ViewModelProvider(requireParentFragment())[HomePagerViewModel::class.java] viewModel.focusEvent.observe(viewLifecycleOwner) { (targetPage, targetPos) -> // 确认当前Fragment对应的页面是目标页面 val pager = (requireParentFragment() as HomePager).viewPager if (pager.currentItem == targetPage) { binding.recyclerView.getChildAt(targetPos)?.requestFocus() } }
方案2:自定义FragmentStateAdapter持有Fragment引用
通过适配器直接保存已创建的Fragment实例,避免依赖标签查找:
2.1 自定义适配器
class HomePagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { private val fragmentCache = mutableMapOf<Int, HomeFragment>() override fun createFragment(position: Int): HomeFragment { val fragment = HomeFragment() fragmentCache[position] = fragment return fragment } override fun getItemCount(): Int = NUM_PAGES // 获取指定位置的Fragment fun getHomeFragment(position: Int): HomeFragment? { return fragmentCache[position] } // 页面销毁时清理缓存,避免内存泄漏 override fun onDestroyView(view: View, viewPager: ViewPager2) { super.onDestroyView(view, viewPager) fragmentCache.clear() } }
2.2 在HomePager中使用适配器获取Fragment
// 初始化适配器 val pagerAdapter = HomePagerAdapter(this) viewPager.adapter = pagerAdapter val psLis: (Char, Int) -> Unit = { direction, pos -> val curItem = viewPager.currentItem val TAG = "psLis" Log.d(TAG, "Page-switch listener initiated.") if (direction == 'l' && curItem > 0) { val targetPage = curItem - 1 viewPager.currentItem = targetPage pagerAdapter.getHomeFragment(targetPage)?.setFocus(pos + 3) } else if (direction == 'r' && curItem < (NUM_PAGES - 1)) { val targetPage = curItem + 1 viewPager.currentItem = targetPage pagerAdapter.getHomeFragment(targetPage)?.setFocus(pos - 3) } }
方案3:使用正确的Fragment标签查找
如果坚持用标签查找,需要使用FragmentStateAdapter的默认标签生成规则:
// 替换原标签生成逻辑 val targetPage = curItem - 1 val fragmentTag = FragmentStateAdapter.createFragmentTag(viewPager.id, targetPage) val curFragment = childFragmentManager.findFragmentByTag(fragmentTag)
注意事项
- 方案1是最稳定的实现,避免了Fragment实例直接耦合,符合Navigation的设计理念。
- 方案2需要在页面销毁时清理缓存,防止内存泄漏。
内容的提问来源于stack exchange,提问作者Tehgan
相关产品推荐
相关产品推荐

