Android ViewPager2+Fragment viewModelScope问题
文章目录
- Android ViewPager2+Fragment viewModelScope问题
- 核心代码
- 问题
- 解决一
- 解决二
- 解决三
Android ViewPager2+Fragment viewModelScope问题
核心代码
class MainActivity : AppCompatActivity() {private lateinit var tabLayout: TabLayoutprivate lateinit var viewPager2: ViewPager2private val titles = mutableListOf<String>()private val fragments = mutableListOf<MyFragment>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)tabLayout = findViewById(R.id.tab_layout)viewPager2 = findViewById(R.id.view_pager2)for (i in 'A'..'F') {titles.add(i.toString())fragments.add(MyFragment.newInstance(i.toString()))}viewPager2.adapter = MyAdapter(this, fragments)viewPager2.offscreenPageLimit = 1TabLayoutMediator(tabLayout,viewPager2,object : TabLayoutMediator.TabConfigurationStrategy {override fun onConfigureTab(tab: TabLayout.Tab,position: Int) {tab.text = titles[position]}}).attach()}class MyAdapter(fragmentActivity: FragmentActivity, val fragments: List<MyFragment>) :FragmentStateAdapter(fragmentActivity) {override fun createFragment(position: Int): Fragment {return fragments[position]}override fun getItemCount(): Int {return fragments.size}}
}
class MyFragment : Fragment() {private val viewModel: MyFragmentViewModel by viewModels()private var title = ""companion object {fun newInstance(title: String) =MyFragment().apply {arguments = Bundle().apply {putString("title", title)}}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)arguments?.let {title = it.getString("title") ?: ""}}override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View {Log.e("TAG", "Fragment ${title} 创建")return inflater.inflate(R.layout.fragment_my, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)val textView = view.findViewById<TextView>(R.id.text_view)textView.text = "Fragment $title"viewModel.load(title)}override fun onDestroyView() {super.onDestroyView()Log.e("TAG", "Fragment ${title} 销毁")}
}
class MyFragmentViewModel : ViewModel() {fun load(title: String) {Log.e("TAG", "$title 开始加载")Log.e("TAG", "$title viewModelScope是否可用:${viewModelScope.isActive}")viewModelScope.launch {delay(1000L)Log.e("TAG", "$title 结束加载")}}
}
问题
首先点击 Fragment A,会执行 Fragment 的 ViewModel 的 load() 方法。接着点击 Fragment F,Fragment A 会被销毁掉,然后再点击 Fragment A,这时 Fragment A会被重新创建,而对应的 ViewModel 的 isActive 却是 false,导致 viewModelScope.launch 无法执行。
解决一
使用 Activity 的ViewModel。
class MyFragment : Fragment() {private val viewModel: MyFragmentViewModel by activityViewModels()
}
解决二
使用 Fragment 中的 lifecycleScope。
class MyFragment : Fragment() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)val textView = view.findViewById<TextView>(R.id.text_view)textView.text = "Fragment $title"viewModel.load(lifecycleScope, title)}
}
解决三
不缓存实例,每次都重新创建 Fragment。
class MainActivity : AppCompatActivity() {private val fragments = mutableListOf<() -> Fragment>()override fun onCreate(savedInstanceState: Bundle?) {for (i in 'A'..'F') {titles.add(i.toString())fragments.add({ MyFragment.newInstance(i.toString()) })}}class MyAdapter(fragmentActivity: FragmentActivity, val fragments: List<() -> Fragment>) :FragmentStateAdapter(fragmentActivity) {override fun createFragment(position: Int): Fragment {return fragments[position].invoke()}override fun getItemCount(): Int {return fragments.size}}
}