Search code examples
androidandroidxfragmentstateadapter

What is purpose of overriding `containsItem` and `getItemId` in `FragmentStateAdapter`?


What is purpose of overriding containsItem and getItemId in FragmentStateAdapter?

Are these overrides required if one is passing in new lists to the FragmentStatePagerAdapter implementation and calling notifyDataSetChanged?

From the doc, appears both overrides are require if the underlying data set is changing, and you need to call notifyDataSetChanged?

/**
 * Default implementation works for collections that don't add, move, remove items.
 * <p>
 * TODO(b/122670460): add lint rule
 * When overriding, also override {@link #getItemId(int)}
 */
public boolean containsItem(long itemId) {
    return itemId >= 0 && itemId < getItemCount();
}

/**
 * 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;
}

So for example, here is my implementation

class MyAdapter(host : Fragment) : FragmentStateAdapter(host) {

    data class FragItem(val newInstance : () -> Fragment, val fragId : Long)

    private var fragments = mapOf<Integer,FragItem>()

    override fun createFragment(position: Int): Fragment {
        return fragments[position]?.newInstance?.invoke() ?: DefaultFragment()
    }
    
    fun applyDataSet(newFragments : Map<Integer, FragItem>) {
        fragments = newFragments
        notifyDataSetChanged()
    }

    override fun getItemCount() = fragments.size

    override fun containsItem(itemId: Long): Boolean{
        return fragments.any { it.value.fragId == itemId }
    }

    override fun getItemId(position: Int) : Long{
        return fragments.values.elementAt(position).fragId
    }
}

Now can I get rid of getItemId and containsItem? Or leave them? What about getItemCount?


Solution

    • getItemCount will instruct the recycler how many items are to be rendered. the current index will be passed as position to createFragment, so clearly you need it
    • the getItemId and containsItem implemented if you want to provide stable ID's for your recycler. having stable ID's allows for a certain optimisations to be made, usually when it involves operations on elements that are not next to each-other in the list.