ViewPager2搭配FragmentStateAdapter如何动态改页面避免整体重绘
ViewPager2 搭配 FragmentStateAdapter 动态修改页面解决方案
核心问题原因
调用notifyDataSetChanged不生效的核心原因是FragmentStateAdapter默认的页面匹配逻辑依赖两个方法:getItemId和containsItem,默认实现是基于position生成id,仅通过id大小判断页面是否存在,修改总页数后Adapter无法识别页面的增删变化,因此不会触发页面更新。
改造后适配器实现
将原适配器中固定的页数变量替换为可动态维护的业务唯一标识列表,同时重写页面匹配方法,封装常用的数据集操作方法:
class TicketsPagerAdapter(f: Fragment, private val ticketIds: MutableList<Int> = mutableListOf()): FragmentStateAdapter(f) { // 返回当前位置对应页面的唯一业务ID,替代默认的position值 override fun getItemId(position: Int): Long { return ticketIds[position].toLong() } // 判断指定ID的页面是否存在于当前数据集 override fun containsItem(itemId: Long): Boolean { return ticketIds.contains(itemId.toInt()) } override fun getItemCount(): Int = ticketIds.size override fun createFragment(position: Int): Fragment { // 传递业务ID而非position,避免页面顺序变化后数据错乱 return TicketsFragment.newInstance(ticketIds[position]) } // 新增单个页面 fun addPage(ticketId: Int) { ticketIds.add(ticketId) notifyItemInserted(ticketIds.size - 1) } // 删除指定位置页面 fun removePage(position: Int) { if (position in ticketIds.indices) { ticketIds.removeAt(position) notifyItemRemoved(position) // 通知后续条目位置变更,避免索引错乱 notifyItemRangeChanged(position, ticketIds.size - position) } } // 清空所有页面 fun clearAllPages() { val oldSize = ticketIds.size ticketIds.clear() notifyItemRangeRemoved(0, oldSize) } // 批量更新页面,DiffUtil自动计算差异,性能最优 fun updatePages(newTicketIds: List<Int>) { val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() { override fun getOldListSize(): Int = ticketIds.size override fun getNewListSize(): Int = newTicketIds.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return ticketIds[oldItemPosition] == newTicketIds[newItemPosition] } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 若业务ID相同则页面内容相同,有自定义判断逻辑可在此修改 return true } }) ticketIds.clear() ticketIds.addAll(newTicketIds) diffResult.dispatchUpdatesTo(this) } }
使用说明
- 常规增删操作直接调用对应封装的
addPage/removePage/clearAllPages方法即可,只会触发对应变化页面的更新,不会重绘全部页面 - 批量修改数据集优先使用
updatePages方法,DiffUtil会自动计算需要新增、删除、移动的页面,性能远高于全量通知 - 不要直接修改内部的
ticketIds列表后调用notifyDataSetChanged,全量通知会额外触发不必要的页面重绘
内容的提问来源于stack exchange,提问作者Jeffrey Blattman
相关产品推荐
相关产品推荐

