问题
I have a viewPager2 and FragmentStateAdapter, and there are Fragement1, 2,3 4, I am in fragment2, and want to remove fragment3, and display fragment4 after fragment2. The problem is it always show me fragment3(data), the debug shows the fragment3 has been removed, but the displayed page still has fragment3 content.
Adpter:
class TipsAdapter(
private val items: MutableList<TripPage>,
context: FragmentActivity
) : FragmentStateAdapter(context) {
private val fragmentFactory = context.supportFragmentManager.fragmentFactory
private val classLoader = context.classLoader
override fun getItemCount(): Int = items.size
override fun createFragment(position: Int): Fragment {
val pageInfo = items[position]
val fragment = fragmentFactory.instantiate(classLoader, pageInfo.fragmentClass.name)
fragment.arguments = Bundle().also { it.putParcelable(PAGE_INFO, pageInfo) }
return fragment
}
fun getFragmentName(position: Int) = items[position].fragmentClass.simpleName
fun removeFragment(position: Int) {
items.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, items.size)
notifyDataSetChanged()
}
}
delete fragment code:
if ((view_pager.adapter as TipsAdapter).getFragmentName(index + 1).equals(
TripPreFragment::class.simpleName) &&
viewModel.shouldRemoveBulkApply()) {
(view_pager.adapter as TipsAdapter).removeFragment(index + 1)
view_pager.setCurrentItem(index + 1, true)
} else {
view_pager.setCurrentItem(index + 1, true)
}
回答1:
Finally, it works for me. when we call notifyDataSetChanged()
, android will call
method getItemId()
in adapter, to check if the item has been updated or not. it returns the position in source code. which means in list 0..i..n
, if you remove i
, it becomes to 0...i...n-1
, the i
won't change and the data won't update in adater.
/**
* Default implementation works for collections that don't add, move, remove items.
* <p>
* TODO(b/122670460): add lint rule
* When overriding, also override {@link #containsItem(long)}.
* <p>
* If the item is not a part of the collection, return {@link RecyclerView#NO_ID}.
*
* @param position Adapter position
* @return stable item id {@link RecyclerView.Adapter#hasStableIds()}
*/
@Override
public long getItemId(int position) {
return position;
}
what you need to do is rewrite this method and containsItem(long)
,
for my case, I use the hashcode of each fragment:
class TipsAdapter(
private val items: MutableList<TripPreferencesOptimizerPage>,
context: FragmentActivity
) : FragmentStateAdapter(context) {
private val fragmentFactory = context.supportFragmentManager.fragmentFactory
private val classLoader = context.classLoader
private val pageIds= items.map { it.hashCode().toLong() }
override fun getItemCount(): Int = items.size
override fun createFragment(position: Int): Fragment {
val pageInfo = items[position]
val fragment = fragmentFactory.instantiate(classLoader, pageInfo.fragmentClass.name)
fragment.arguments = Bundle().also { it.putParcelable(PAGE_INFO, pageInfo) }
return fragment
}
fun getFragmentName(position: Int) = items[position].fragmentClass.simpleName
fun removeFragment(position: Int) {
items.removeAt(position)
notifyItemRangeChanged(position, items.size)
notifyDataSetChanged()
}
override fun getItemId(position: Int): Long {
return items[position].hashCode().toLong() // make sure notifyDataSetChanged() works
}
override fun containsItem(itemId: Long): Boolean {
return pageIds.contains(itemId)
}
}
来源:https://stackoverflow.com/questions/57938930/remove-fragment-in-viewpager2-use-fragmentstateadapter-but-still-display