/** * If you don't specify how to handle payloads in your implementation, they'll be ignored and * the adapter will do a full rebind. * * @param viewHolder The ViewHolder to bind * @param position The adapter position * @param payloads A list of payloads (may be empty) */ public void bind(@NonNull VH viewHolder, int position, @NonNull List<Object> payloads) { bind(viewHolder, position); }
@Override @NonNull public VH onCreateViewHolder(@NonNull ViewGroup parent, int layoutResId) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); Item<VH> item = getItemForViewType(layoutResId); View itemView = inflater.inflate(layoutResId, parent, false); return item.createViewHolder(itemView); }
@Nullable @Override public Object getChangePayload(int oldItemPosition, int newItemPosition) { Item oldItem = getItem(oldBodyGroups, oldItemPosition); Item newItem = getItem(newBodyGroups, newItemPosition); return oldItem.getChangePayload(newItem); } });
/** * Whether two item objects represent the same underlying data when compared using DiffUtil, * even if there has been a change in that data. * <p> * The default implementation compares both view type and id. */ public boolean isSameAs(Item other) { if (getLayout() != other.getLayout()) { return false; } return getId() == other.getId(); }
@Override public void onItemClick(@NonNull Item item, @NonNull View view) { if (item instanceof LabelDialogItem) { LabelDialogItem labelDialogItem = (LabelDialogItem) item; labelDialogItem.toggleSelected(); selectedChoices[adapter.getAdapterPosition(item)] = labelDialogItem.isSelected(); item.notifyChanged(); } } }
@Test public void notifyChangeNotifiesParentObserver() { Item item = new DummyItem(); item.registerGroupDataObserver(groupAdapter); item.notifyChanged(); verify(groupAdapter).onItemChanged(item, 0); } }
@Test public void selfPositionIs0() throws Exception { Item item = new DummyItem(); Assert.assertEquals(0, item.getPosition(item)); }
@Override public int getItemViewType(int position) { lastItemForViewTypeLookup = getItem(position); if (lastItemForViewTypeLookup == null) throw new RuntimeException("Invalid position " + position); return lastItemForViewTypeLookup.getLayout(); }
public int getDragDirs() { return item.getDragDirs(); }
public @NonNull Map<String, Object> getExtras() { return item.getExtras(); }
@Override public int getSpanSize(int position) { try { return getItem(position).getSpanSize(spanCount, position); } catch (IndexOutOfBoundsException e) { // Bug in support lib? TODO investigate further return spanCount; } } };
@Override public long getItemId(int position) { return getItem(position).getId(); }
@Test public void changeAnItemNotifiesChange() { List<Item> children = new ArrayList<Item>(); Item item = new DummyItem(); children.add(item); UpdatingGroup group = new UpdatingGroup(); group.update(children); group.registerGroupDataObserver(groupAdapter); item.notifyChanged(); verify(groupAdapter).onItemChanged(group, 0); }
@Test public void positionIsNegative1IfItemIsNotSelf() throws Exception { Item item = new DummyItem(); Item differentItem = new DummyItem(); Assert.assertEquals(-1, item.getPosition(differentItem)); }
/** * This idea was copied from Epoxy. :wave: Bright idea guys! * <p> * Find the model that has the given view type so we can create a viewholder for that model. * <p> * To make this efficient, we rely on the RecyclerView implementation detail that {@link * GroupAdapter#getItemViewType(int)} is called immediately before {@link * GroupAdapter#onCreateViewHolder(android.view.ViewGroup, int)}. We cache the last model * that had its view type looked up, and unless that implementation changes we expect to have a * very fast lookup for the correct model. * <p> * To be safe, we fallback to searching through all models for a view type match. This is slow and * shouldn't be needed, but is a guard against RecyclerView behavior changing. */ private Item<VH> getItemForViewType(@LayoutRes int layoutResId) { if (lastItemForViewTypeLookup != null && lastItemForViewTypeLookup.getLayout() == layoutResId) { // We expect this to be a hit 100% of the time return lastItemForViewTypeLookup; } // To be extra safe in case RecyclerView implementation details change... for (int i = 0; i < getItemCount(); i++) { Item item = getItem(i); if (item.getLayout() == layoutResId) { return item; } } throw new IllegalStateException("Could not find model for view type: " + layoutResId); }
@Override public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) { Item contentItem = getItem(position); contentItem.bind(holder, position, payloads, onItemClickListener, onItemLongClickListener); }
@Test public void notifyChangeInAnItemCausesParentToNotifyChange() { List<Item> children = new ArrayList<Item>(); Item item = new DummyItem(); children.add(item); Section group = new Section(); group.setHeader(new DummyItem()); group.update(children); group.registerGroupDataObserver(groupAdapter); item.notifyChanged(); verify(groupAdapter).onItemChanged(group, 1); }
@Nullable @Override public Object getChangePayload(int oldItemPosition, int newItemPosition) { Item oldItem = getItem(oldGroups, oldItemPosition); Item newItem = getItem(newGroups, newItemPosition); return oldItem.getChangePayload(newItem); } }
public abstract void bind(@NonNull VH viewHolder, int position);