Android使用ViewPager时调用findFragmentByTag持续返回Null的解决办法
问题根源
FragmentStateAdapter和废弃的FragmentPagerAdapter内部生成Fragment标签的规则完全不同,旧的android:switcher:前缀的标签规则仅适用于废弃的PagerAdapter系列,FragmentStateAdapter不会给生成的Fragment设置这类标签,所以调用findFragmentByTag必然返回null。
推荐解决方案:共享ViewModel
这是Google官方推荐的Activity与子Fragment通信方案,无需手动查找Fragment引用,也可规避生命周期不同步导致的空指针问题。
步骤1:创建共享ViewModel类
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class SharedTimeViewModel : ViewModel() { // 封装要传递的时间数据 val timeData = MutableLiveData<Triple<String, String, String>>() }
步骤2:Activity中发送数据
class MainActivity : AppCompatActivity(), StopwatchListener { // 初始化和当前Activity绑定的ViewModel private val sharedViewModel: SharedTimeViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) // 原有初始化逻辑不变 binding.viewPagerMain.adapter = PageAdapter(supportFragmentManager, lifecycle) } override fun getTheTime(min: String?, sec: String?, millis: String?) { // 直接更新ViewModel的LiveData,无需查找Fragment if(min != null && sec != null && millis != null) { sharedViewModel.timeData.value = Triple(min, sec, millis) } } }
步骤3:StopwatchFragment中接收数据
class StopwatchFragment : Fragment() { // 绑定和宿主Activity共有的ViewModel实例 private val sharedViewModel: SharedTimeViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 观察数据变化,自动触发UI更新 sharedViewModel.timeData.observe(viewLifecycleOwner) { (min, sec, millis) -> displayData(min, sec, millis) } } fun displayData(min: String, sec: String, millis: String) { // 原有展示逻辑不变 } }
备选方案:手动管理Fragment引用
如果不想引入ViewModel,也可以自行在Adapter中存储已创建的Fragment引用,注意要处理Fragment销毁重建的场景,避免内存泄漏。
步骤1:修改PageAdapter实现
class PageAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) { // 用SparseArray存储Fragment引用,自动适配稀疏下标场景 val fragments = SparseArray<Fragment>() override fun getItemCount() = 3 override fun createFragment(position: Int): Fragment { val fragment = when (position) { 0 -> RepsFragment() 1 -> StopwatchFragment() 2 -> TimerFragment() else -> RepsFragment() } // 存储新建的Fragment引用 fragments.put(position, fragment) return fragment } // 页面销毁时移除引用,避免内存泄漏 override fun onDestroyed(container: ViewGroup, position: Int, `object`: Any) { super.onDestroyed(container, position, `object`) fragments.remove(position) } }
步骤2:Activity中直接调用Fragment方法
override fun getTheTime(min: String?, sec: String?, millis: String?) { val currentPos = binding.viewPagerMain.currentItem val targetFragment = (binding.viewPagerMain.adapter as PageAdapter).fragments.get(currentPos) if(targetFragment is StopwatchFragment && min != null && sec != null && millis != null) { targetFragment.displayData(min, sec, millis) } }
注意:备选方案中如果ViewPager未开启足够的offscreenPageLimit,非当前页的Fragment被销毁后引用会被自动清空,调用前需做空校验。
内容的提问来源于stack exchange,提问作者DJ. Aduvanchik
相关产品推荐
相关产品推荐

