Android Kotlin中如何实现抽屉流畅关闭同时异步更新RecyclerView?
解决DrawerLayout关闭卡顿+异步更新RecyclerView/Fragment的方案
核心问题
DrawerLayout的关闭动画在主线程执行,而DiffUtil的差异计算、后续的UI绑定或Fragment事务也会占用主线程,两者冲突导致卡顿。下面是几个直接可用的解决思路:
1. 等抽屉完全关闭后再执行更新操作
直接监听DrawerLayout的关闭回调,等动画彻底结束后再触发数据提交或Fragment切换,从根源避免主线程冲突:
drawerLayout.addDrawerListener(object : DrawerLayout.SimpleDrawerListener() { override fun onDrawerClosed(drawerView: View) { super.onDrawerClosed(drawerView) // 这里执行列表更新或Fragment切换 adapter.submitList(newDataList) // 或者Fragment切换 supportFragmentManager.beginTransaction() .replace(R.id.container, targetFragment) .setReorderingAllowed(true) .commit() } }) // 抽屉点击时只触发关闭,不做其他操作 drawerMenuItem.setOnClickListener { drawerLayout.closeDrawer(GravityCompat.START) // 可以提前在这里异步加载数据,比如从ViewModel请求 viewModel.loadTargetData() }
如果数据加载需要时间,建议在点击抽屉项时就开始异步请求数据,等抽屉关闭后数据刚好准备好,直接提交,既不耽误时间也不卡动画。
2. 让DiffUtil的计算完全脱离主线程
DiffUtil的calculateDiff方法本身是同步的,默认在主线程执行会阻塞,所以要把计算逻辑放到后台线程,计算完再切回主线程提交:
// 在ViewModel或Repository里异步计算差异 viewModelScope.launch(Dispatchers.Default) { val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() { override fun getOldListSize() = oldList.size override fun getNewListSize() = newList.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldList[oldItemPosition].id == newList[newItemPosition].id } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldList[oldItemPosition] == newList[newItemPosition] } }) // 切回主线程提交结果 withContext(Dispatchers.Main) { adapter.submitList(newList) // 如果是Fragment切换,同样在这里执行(如果需要等数据准备好) } }
注意:adapter.submitList必须在主线程调用,因为它会触发RecyclerView的UI更新。
3. 优化RecyclerView的Item绑定性能
如果Item布局复杂或者bind方法里有耗时操作,哪怕异步计算了Diff,绑定的时候还是会卡主线程:
- 尽量减少Item布局的层级,用
merge标签减少嵌套 - 在ViewHolder里提前缓存View引用(用ViewBinding自动生成的缓存更高效)
- 避免在
onBindViewHolder里做IO操作、复杂计算,这些都提前放到ViewModel里处理 - 开启RecyclerView的预加载:
recyclerView.setItemViewCacheSize(10)(根据Item数量调整)
4. Fragment切换的额外优化
如果是切换Fragment,除了等抽屉关闭再执行,还可以给事务加上setReorderingAllowed(true),让系统优化Fragment的切换流程,减少主线程压力:
supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, TargetFragment()) .setReorderingAllowed(true) .commit()
内容的提问来源于stack exchange,提问作者Deepesh kumar
相关产品推荐
相关产品推荐

