You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

集成Jetpack Navigation后,如何获取ViewPager2当前页面Fragment实例?

问题分析

引入Jetpack Navigation后,ViewPager2关联的Fragment管理逻辑发生了两个关键变化:

  1. Navigation的NavHostFragment会接管导航栈内Fragment的生命周期管理,导致HomePager的childFragmentManager不再直接持有ViewPager2的页面Fragment(你观察到childFragmentManager.fragments为空就是这个原因)。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 08:00:36