-
Notifications
You must be signed in to change notification settings - Fork 546
5.x | Headers and Sections
- Section
- Initialization
- Special features:
A Section is composed by a Header item with IHeader
interface and a Child item with ISectionable
interface which internally holds the same instance of the header item. Read the tutorial of Item interfaces for more details about all item interfaces.
For simplicity, you can directly extend AbstractHeaderItem
and AbstractSectionableItem
respectively to quickly create a header item and a child item with header. Optionally you can extend/collapse a section (please refer at the end of this page):
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);
}
}
public abstract class AbstractSectionableItem<VH extends RecyclerView.ViewHolder, T extends IHeader>
extends AbstractFlexibleItem<VH>
implements ISectionable<VH, T> {
/**
* The header of this item
*/
protected T header;
public AbstractSectionableItem(T header) {
this.header = header;
}
@Override
public T getHeader() {
return header;
}
@Override
public void setHeader(T header) {
this.header = header;
}
}
There are 2 use cases to represent sections with headers:
Main items are defined as ISectionable
that hold an IHeader
item, useful to display 1 or more items with the same Header linked.
The adapter list should be initialized only with ISectionable
items, while the Header item, not added to the list, must be assigned to the ISectionable
items of the same group, using the constructor or the method setHeader()
.
Header items can be shown all together with a unique command showAllHeaders()
OR at startup by calling setDisplayHeadersAtStartUp(true)
which internally calls showAllHeaders()
. Doing this, the hidden property is changed to false
and the header items are inserted at the top of each ISectionable
item, wherever the header is still hidden.
Note:
- By default, headers are NOT shown at startup.
- As explained in the Item interfaces, it is important to well Override the
equals
method.
Header item is defined as IExpandable
combined with IHeader
, containing sub items of type ISectionable
. For simplicity, you can directly extend from AbstractExpandableHeaderItem
.
Here, you should add only the expandable/header items to the Adapter list.
To initially expand the section at startup, you need to call expandItemsAtStartUp()
in the Activity/Fragment creation phase.
Note:
- Headers items can be hidden, but be aware that sub items need the own parent (Header) currently shown.
To make a header sticky, you must follow these 5 steps:
At design time
1. Always wrap the RecyclerView widget with a FrameLayout as following:
<!-- This FrameLayout is still mandatory ONLY IF sticky header will be enabled. This
layout will help to receive the inflated layout at runtime and to display the
Refresh circle AND the FastScroll on the top of sticky_header_layout. -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/recycler_adapter_item"/>
<!-- NOT NEEDED ANYMORE!! This layout is generated at runtime when
sticky headers are enabled. So you can safely remove it. -->
<!--<include layout="@layout/sticky_header_layout"/>-->
</FrameLayout>
Note: You can still customize your
sticky_header_layout
by calling:
setStickyHeaders(true, ViewGroup stickyContainer).
2. Implement IHeader
interface for the item that will be sticky.
3. You need to pass true
to the super() constructor of the Header ViewHolder only. Since itemView
is declared final by Android, this parameter allows to insert a FrameLayout
holding the real content. This is necessary to avoid App crashing during scrolling (etc.) because the LayoutManager needs the itemView
with a contentView filled. The technique adopted actually moves the contentView to the FrameLayout (@layout/sticky_header_layout) changing the parent view!
At runtime
4. Display headers with setDisplayHeadersAtStartUp(true)
.
5. Call setStickyHeaders(true)
, headers stay sticky if they are currently shown.
Note: You should call
getFlexibleAdapterPosition()
instead ofgetAdapterPosition()
only in Header View Holders. For 2 reasons:
getAdapterPosition()
is declared final and we cannot override it;
- When scrolling down the RecyclerView, the sticky ViewHolders are created out of the LayoutManager and if clicked in this particular situation, NO_POSITION is returned!
It is super easy to add the transparency and elevation to the header item, the values are taken from the header item layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#aa7e57c2" <--- transparent background
android:elevation="5dp"> <--- elevation for header item + sticky header
...
</RelativeLayout>
But if elevation is not configured in the layout or is Zero, then you have 1 new method to obtain the following effect: header item flat + sticky header elevated:
setStickyHeaderElevation(float stickyElevation);
Bonus from my researches:
- Transparent Sticky Header with elevation at the same time
-
Surgery precision on header swapping :-)
- Double background not visible anymore on header swapping, if transparency is set.
- Fade animation on sticky header container, when sticky is enabled or disabled.
- To add new section:
- You can call
addSection(header)
. An empty section (the header) will be added to the top of the list; - You can call
addSection(header, comparator*)
. To add the section in the middle of the list with your custom sorting rules.
- You can call
- To remove a section:
- You can delete the header item with
removeItem(getGlobalPositionOf(header));
. - You will have to handle yourself the items not belonging anymore to that header. By default, items keep the link of header, but you can change the default behavior by calling
setUnlinkAllItemsOnRemoveHeaders(true)
.
- You can delete the header item with
- Add the new
IExpandable+IHeader
item to the Adapter list by simply callingaddSection()
oraddItem(position, item)
. This section remains collapsed until you callexpand(the new position)
, when you do, be sure to have theexpanded
property equals tofalse
. Optionally, this property can remaintrue
, but you have to callexpand(the new position, init = true)
: read carefully the JavaDoc. - Deleting the
IExpandable
item, will automatically collapse the expandable and removed from the adapter list together with the sub-items too!
Always keep in mind that, the Adapter list must be manually synchronized with your Database list, meaning that the new item already exists in the Database list (if not, you have to add it first), then you call the Adapter methods to reflect the change and animate it for the user.
To add sub items:
You can call addItemToSection(sectionable, header, comparator*)
and to simplify the process calculatePositionFor(item, comparator*)
has been introduced, this method accepts a custom Comparator
object to extract the precise position where to add the new item in the Adapter list, then call addItem(position, item)
.
Simply call addSubItem(parentPosition, subPosition, subItem)
. Section must be already expanded, otherwise it doesn't have any effect, if not, add true
as 4th parameter it will be automatically expanded. Example:
public void addChild(AbstractExpandableHeaderItem header) {
ChildItem childItem = ...
//Add the child item to the end of the section
//Note: you don't need the next line, if childItem is already part of the sublist
header.addSubItem(childItem);//last position
//Synchronize the Adapter,
mAdapter.addSubItem(mAdapter.getGlobalPositionOf(header),
header.getSubItemPosition(childItem),
childItem);
}
To remove any items, simply call removeItem(the adapter global position)
. If you have the Undo functionality enabled, you then delete the item from the Database when timeout is over (See UndoHelper
class).
* = The
Comparator
object should be customized to support ALL the types of items the Adapter is displaying or a ClassCastException will be raised. If the Comparator isnull
the returned position will be 0. See the demo App for a real example.
We can drag an ISectionable
item with a header inside or the IHeader
item as well. Auto-linkage works in 6 ways:
- Header item is dragged up.
- Header item is dragged down.
- The upper item is dragged below the header item.
- The underlying item is dragged above the header.
- The underlying item is dragged lower.
- A lower item is dragged up and becomes a new underlying item.
The ISectionable item is updated with the following behaviors:
- Item moved out of any section has a null header.
- Item moved into another section updates the header.
- Item moved inside the same section maintains the header.
The header item is notified about the change and if the underlying item is another header, then the section becomes empty and header remains unlinked.
Using AbstractExpandableHeaderItem
(in the library), an implementation of IExpandable
and IHeader
interfaces, it is possible to make a section expandable/collapsible by configuring the item signature as following (3 examples). This special item can be collapsed by clicking on it, also when the header of the section (aka parent) is sticky!
Example 1:
public class ExpandableHeaderItem
extends AbstractExpandableHeaderItem<ExpandableHeaderItem.ExpandableHeaderViewHolder, SubItem> {
public ExpandableHeaderItem() {
super();//Call super to auto-configure the section status as shown, expanded, not selectable
}
}
Example 2:
public class ExpandableHeaderItem
extends AbstractFlexibleItem<ExpandableHeaderItem.ExpandableHeaderViewHolder>
implements IExpandable<ExpandableHeaderItem.ExpandableHeaderViewHolder, SubItem>,
IHeader<ExpandableHeaderItem.ExpandableHeaderViewHolder> {
public ExpandableHeaderItem() {
//Configure the section status: shown and/or expanded
setHidden(false);
setExpanded(true);
//NOT selectable, otherwise (if you have) the ActionMode will be activated on long click!
setSelectable(false);
}
}
Example 3:
public class ExpandableHeaderItem
extends AbstractExpandableItem<ExpandableHeaderItem.ExpandableHeaderViewHolder, SubItem>,
implements IHeader<ExpandableHeaderItem.ExpandableHeaderViewHolder> {
public ExpandableHeaderItem() {
setHidden(false);
setExpanded(true);
setSelectable(false);
}
}
- 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!