Skip to content

5.x | Item interfaces

Davide Steduto edited this page May 1, 2018 · 57 revisions

In this page


In this page we talk about in the detail of each item interface. For the initial setup please refer to the wiki page Setting Up.

Introduction

Item interfaces are considered presentation items and have been introduced to simplify and better organize the code when it comes the necessity to support different item types that are going to be displayed in the RecyclerView. Not less, they add life to the item so they can be sticky, expanded, touched, etc.. so they can responds to the user actions.

Basically, the Adapter automatically recognizes the view type by mapping the item in a small HashMap, this is done in getItemViewType(). It then delegates the creation + binding of the ViewHolder to the item itself:

Item delegation


ℹ️ Note: All the interfaces are modular and can be combined to add functionalities in the same item.

Possible combinations

Item type to display Item interfaces to implement
Simple adapter item IFlexible (AbstractFlexibleItem)
Expandable item IFlexible + IExpandable (AbstractExpandableItem)
SubItem for expandable IFlexible (AbstractFlexibleItem)
Header item (with sticky functionality) IFlexible + IHeader (AbstractHeaderItem)
Expandable header item (with sticky functionality) IFlexible + IExpandable + IHeader (AbstractExpandableHeaderItem)
Section simple item for header IFlexible + ISectionable (AbstractSectionableItem)
Section SubItem for expandable header IFlexible + ISectionable (AbstractSectionableItem)
Section expandable item for header IFlexible + ISectionable + IExpandable (AbstractExpandableItem + ISectionable)
Add filter functionality to any previous item ... + IFilterable
Add Model holder functionality to any previous item ... + IHolder

Primary item interfaces

These items holds some flags, necessary to the Adapter to read the current state of the object.

IFlexible

Basic interface to identify a generic Adapter item. This type returns information for an item to be enabled/disabled, selectable, hidden and touchable. This item also defines the methods for the ViewHolder. In the next wiki page ViewHolders we talk about the creation and the binding of a ViewHolder more in details.

Implement this interface or use AbstractFlexibleItem.

public interface IFlexible<VH extends RecyclerView.ViewHolder> {

    /*---------------*/
    /* BASIC METHODS */
    /*---------------*/

    /**
     * When an item is disabled all events such as click
     * listeners are blocked. So it cannot be selected,
     * expanded, dragged or swiped, etc..
     */
    boolean isEnabled();

    void setEnabled(boolean enabled);

    /**
     * (Internal usage).
     * When and item has been deleted (with Undo) or has been filtered out by the
     * adapter, then, it has hidden status.
     */
    boolean isHidden();

    void setHidden(boolean hidden);

    /**
     * Individual item's span size.
     */
    int getSpanSize(int spanCount, int position);

    /**
     * Called by the FlexibleAdapter when it wants to check if this
     * item should be bound again with new content. You should
     * return false if you want this item skips binding on updates.
     */
    boolean shouldNotifyChange(IFlexible newItem);

    /*--------------------*/
    /* SELECTABLE METHODS */
    /*--------------------*/

    /**
     * An item not selectable cannot be added in the selection list,
     * but continues to respond to the click events, etc..
     */
    boolean isSelectable();

    void setSelectable(boolean selectable);

    /**
     * Custom bubble text for FastScroller.
     */
    String getBubbleText(int position);

    /*-------------------*/
    /* TOUCHABLE METHODS */
    /*-------------------*/

    boolean isDraggable();

    void setDraggable(boolean draggable);

    boolean isSwipeable();

    void setSwipeable(boolean swipeable);

    /*---------------------*/
    /* VIEW HOLDER METHODS */
    /*---------------------*/

    /**
     * Identifies a specific view type for this item, used by FlexibleAdapter to auto-map
     * the ViewTypes.
     */
    int getItemViewType();

    /**
     * Returns the layout resource ID to auto-inflate the View for this item. Optionally,
     * you can assign same layout for multiple item types, but getItemViewType() must
     * return unique values!
     * NOTE: Should identify a resource Layout reference android.R.layout.
     */
    @LayoutRes
    int getLayoutRes();

    /**
     * Creation of the ViewHolder. The view is already inflated!
     */
    VH createViewHolder(View view, FlexibleAdapter<IFlexible> adapter);

    /**
     * Binds the data of this item to the given Layout.
     */
    void bindViewHolder(FlexibleAdapter<IFlexible> adapter, VH holder, int position, 
                        List<Object> payloads);

    /**
     * Called by RV when a view created by this adapter has been recycled.
     */
    void unbindViewHolder(FlexibleAdapter<IFlexible> adapter, VH holder, int position);
}

AbstractFlexibleItem

This Abstract implementation is nothing more than a class that holds the boolean flags and where the equals method becomes mandatory. Default values are mEnabled = true, mHidden = false, mSelectable = true, mDraggable = true, mSwipeable = true.
If you implement directly from IFlexible, you are instead responsible to provide these values to the Adapter.

IExpandable

Interface to identify if an item is able to expand/collapse. In this item we define also the type of the SubItems that are ideally saved in a List. This item type is mainly (and most of the times) intended to be a main item with inside some items as specifications of the expandable. This is the reason why you are going to add only IExpandable items to the adapter list and not the sub items that are displayed or hidden by user or by calling specific functions at runtime.

Implement this interface or use AbstractExpandableItem.

ℹ️ Note: This item already inherits all the methods from IFlexible interface.

public interface IExpandable<VH extends ExpandableViewHolder, S extends IFlexible>
        extends IFlexible<VH> {

    /*--------------------*/
    /* EXPANDABLE METHODS */
    /*--------------------*/

    boolean isExpanded();

    void setExpanded(boolean expanded);

    /**
     * Establish the level of the expansion of this type of item in case of multi
     * level expansion.
     * Default value of first level should return 0.
     * Sub expandable items should return a level +1 for each sub level.
     */
    int getExpansionLevel();

    /*-------------------*/
    /* SUB ITEMS METHODS */
    /*-------------------*/

    List<S> getSubItems();
}

AbstractExpandableItem

Generic implementation that contains the list of the SubItems and a set of useful methods to handle this list, contains also the boolean flag for the expanded status, plus all the flags inherited from AbstractFlexibleItem. Default values are mExpanded = false, mSubItems = null.
If you implement directly from IExpandable, you are instead responsible to provide these values to the Adapter.

ℹ️ Note:

  • The ViewHolder must be of type ExpandableViewHolder to benefit of the expandable methods. Please see the JavaDoc for the entire list of methods this Class is implementing.
  • In order to expand/collapse an item, you must add a click listener to the Adapter or alternatively implement the click listener to a specific inner view of the item and call toggleExpansion() method in the ExpandableViewHolder as done in the overridden onClick.

IHeader

Wrapper empty interface to identify if the current item is a Header. In the abstract implementation, this item is hidden and not selectable, it means that, Header items are mainly (and most of the times) intended to be decoration items to support the main items that section represents and not part of the realm of all main items. This is the reason why you are going to add only ISectionable items to the adapter list and not IHeader items that are all displayed or hidden by calling specific functions.

Implement this interface or use AbstractHeaderItem or AbstractExpandableHeaderItem.

ℹ️ Note:

  • If you combined IExpandable with IHeaders, the expandable has always the precedence on the header, therefore you should compose the adapter list always with expandable and not with sectionable sub items.
  • The ViewHolder must be of type FlexibleViewHolder to assure correct Sticky Header behaviours and you are also required to specify sticky=true to the super ViewHolder constructor.
public interface IHeader<VH extends FlexibleViewHolder> extends IFlexible<VH> {
}

AbstractHeaderItem

Generic implementation of IHeader interface. By default this item is hidden and not selectable for the reason explained above.

Implement this interface or use AbstractExpandableHeaderItem.

ℹ️ Note: The class already inherits all the fields and the methods from AbstractFlexibleItem.

public abstract class AbstractHeaderItem<VH extends FlexibleViewHolder>
        extends AbstractFlexibleItem<VH>
        implements IHeader<VH> {

    /**
     * By default, header is hidden(!) and not selectable(!)
     */
    public AbstractHeaderItem() {
        setHidden(true);
        setSelectable(false);
    }
}

AbstractExpandableHeaderItem

Complex generic implementation of IExpandable interface combined with IHeader interface with useful methods inherited from AbstractExpandableItem to manage expandable sections with sticky headers and sub items of type ISectionable. By default, expandable header is shown, expanded and not selectable.

ℹ️ Note:

  • The class already inherits all the fields and the methods from AbstractExpandableItem.
  • The ViewHolder must be of type ExpandableViewHolder.
public abstract class AbstractExpandableHeaderItem<VH extends ExpandableViewHolder, S extends ISectionable>
        extends AbstractExpandableItem<VH, S>
        implements IHeader<VH> {

    /**
     * By default, expandable header is shown, expanded and not selectable.
     */
    public AbstractExpandableHeaderItem() {
        setHidden(false);
        setExpanded(true);
        setSelectable(false);
    }
}

ISectionable

Interface that represents an item in a section. Implement this interface or use AbstractSectionableItem.

public interface ISectionable<VH extends RecyclerView.ViewHolder, T extends IHeader>
        extends IFlexible<VH> {

    T getHeader();

    void setHeader(T header);
}

AbstractSectionableItem

Generic implementation of ISectionable interface for items that holds the reference of the header item. If you implement directly from ISectionable, you are instead responsible to provide the header reference to the Adapter.

Additional item interfaces

These items, instead give some additional features to each item.

IFilterable

By adding this interface to the item signature, the item becomes automatically filterable and when the user starts a search, the method filter() is called by the Adapter. If an item doesn't implement this interface, the filter ignores it automatically.

public interface IFilterable<F extends Serializable> {

    /**
     * Checks and performs the filter on this item, you can apply the logic
     * and the filter on every fields your use case foresees.
     *
     * @param constraint the search text typed by the user
     * @return true if this item should be collected by the Adapter for the
     * filtered list, false otherwise
     */
    boolean filter(F constraint);
}

IHolder

This special item interface comes in help in case:

  • you need item serialization/deserialization of the data model.
  • you need to display the same data model in multiple RecyclerViews managed by different Adapters.
  • you want or are obliged to keep the model data as it is, because already extends other frameworks. Therefore, it HOLDS the reference of your data model object!
    We separate the real Model object from the Adapter item by using the IHolder interface as following:
/**
 * In this way you can separate the memory zones of the flags (enabled, expanded, hidden,
 * selectable, draggable, swipeable, etc...) used by an Adapter, to be independent by
 * another Adapter or Framework. For instance, an item can be Shown and Expanded in a RV,
 * while in the other RV can be Hidden or Not Expanded at the same time!
 */
public class CustomItemHolder<Model>
         extends AbstractSectionableItem<FlexibleItemHolder.ViewHolder, HeaderItem>
         implements IFilterable, IHolder<Model> {

    /**
     * Your complex data model object
     */
    Model modelData;

    public CustomItemHolder(Model modelData, HeaderItem header) {
        super(header);
        this.modelData = modelData;
    }

    @Override
    public Model getModel() {
        return modelData;
    }
    ...
}

ℹ️ Note: equals() and filter() methods should take into account the fields of Model class.

The importance of equals and hashCode methods

Equals

It is important to correctly Override the mandatory equals method of IFlexible. Have a look at this page Writing a correct equals method to implement your own equals method.

You mainly need to implement this method when you have unique IDs for your items. If not, you can rely on default Java implementation return this == o.

The risk, of a wrong result, is when the Adapter is looking for a reference (Header or sub item) the element is not found and the expected behavior doesn't occur. The equals() method, which do not check the type of their parameter, may result in latent errors quite difficult to discover!

HashCode

You should implement also this method if equals is implemented. This method has several implications that Adapter can handle better:

  • The Hash, significantly increases performance in big list during Update & Filter operations, better than the new Android(R) class DiffUtil!
  • You might want to activate stable ids via Constructor for RecyclerView, only if your id is unique and benefit of the animations also when notifyDataSetChanged() is invoked. More on stable ids? Check issue comment #230 or google it.

Have a look at this page Writing a correct hashCode method to implement your own hashCode method.

Very simple equals and hashCode implementation may be:

public class Item extends AbstractFlexibleItem<Item.ViewHolder> {

    private String id;

    /**
     * When an item is equals to another?
     * Write your own concept of equals, mandatory to implement.
     */
    @Override
    public boolean equals(Object inObject) {
        if (inObject instanceof HeaderItem) {
            HeaderItem inItem = (HeaderItem) inObject;
            return this.id.equals(inItem.id);
        }
        return false;
    }

    //... or Java default if you don't have unique IDs.

    @Override
    public boolean equals(Object o) {
        return this == o;
    }

    /**
     * Override this method too, when using functionalities like Filter
     * and stableIds. FlexibleAdapter is making use of HashSet to
     * improve performancein big list.
     */
    @Override
    public int hashCode() {
        return id.hashCode();
    }
}

Clone this wiki locally