-
Notifications
You must be signed in to change notification settings - Fork 554
5.x | Item interfaces
- Introduction
- Possible combinations
- Primary item interfaces
- Additional item interfaces
- The importance of
equals
andhashCode
methods
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.
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:
Note: All the interfaces are modular and can be combined to add functionalities in the same item.
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 |
These items holds some flags, necessary to the Adapter to read the current state of the object.
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);
/**
* When and item is deleted (with Undo) or is 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);
/*-------------------*/
/* TOUCHABLE METHODS */
/*-------------------*/
boolean isDraggable();
void setDraggable(boolean draggable);
boolean isSwipeable();
void setSwipeable(boolean swipeable);
/*---------------------*/
/* VIEW HOLDER METHODS */
/*---------------------*/
/**
* Returns the layout resource Id to AutoMap a specific ViewType on this Item.
* NOTE: Should identify a resource Layout reference "android.R.layout" used
* by FlexibleAdapter to AutoMap the ViewTypes.
*/
@LayoutRes
int getLayoutRes();
/**
* Creation of the ViewHolder. The view is already inflated!
*/
VH createViewHolder(View view, FlexibleAdapter adapter);
/**
* Binds the data of this item to the given Layout.
*/
void bindViewHolder(FlexibleAdapter adapter, VH holder, int position, List payloads);
/**
* Called by RV when a view created by this adapter has been recycled.
*/
void unbindViewHolder(FlexibleAdapter adapter, VH holder, int position);
}
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 = false
, mSwipeable = false
.
If you implement directly from IFlexible
, you are instead responsible to provide these values to the Adapter.
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();
}
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.
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
withIHeaders
, 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.
Note: The ViewHolder must be of type
FlexibleViewHolder
to assure correct Sticky Header behaviours and you are also required to specifysticky=true
to the super ViewHolder constructor.
public interface IHeader<VH extends FlexibleViewHolder> extends IFlexible<VH> {
}
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);
}
}
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
.
Note: 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);
}
}
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);
}
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.
These items, instead give some additional features to each item.
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 {
/**
* 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(String constraint);
}
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 theIHolder
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()
andfilter()
methods should take into account the fields ofModel
class.
It is important to well 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!
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();
}
}
- Update Data Set
- Selection modes
- Headers and Sections
- Scrollable Headers and Footers
- Expandable items
- Drag&Drop and Swipe
- EndlessScroll / On Load More
- Search Filter
- FastScroller
- Adapter Animations
- Third party Layout Managers
- Payload
- Smooth Layout Managers
- Flexible Item Decoration
- Utils
- ActionModeHelper
- AnimatorHelper
- EmptyViewHelper
- UndoHelper
* = Under revision!