-
Notifications
You must be signed in to change notification settings - Fork 551
5.x | Headers and Sections
- Section
- Initialization
- Special features:
A Section is composed by a Header item with IHeader
interface and a Child item or a set of Children items of type ISectionable
which internally hold 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 its 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 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 same instance of the Header item, not added to the list, must be assigned to all 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 same header instance is still hidden.
For sectioned(headered) Grid
you should pass a GrigLayoutManager
to a RecyclerView
and override getSpanSize(int spanCount, int position)
of your IHeader
implementation to return actual span (by default it returns 1 and thus your headers will look like grid items). You can pass span size in to your IHeader
class constructor if it does not change at runtime. If span size changes dynamically please refer to a sample app.
⚠️ Warning:
- 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, on the other hand, 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 FastScroller 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>
2. Implement IHeader
interface for the item that will be sticky.
3. Since itemView
is declared final in Android SDK, you will need to pass true
to the super() constructor of the Header ViewHolder only.
This parameter allows to insert a FrameLayout
holding the real content and 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!
Therefore, to access to the header content you must call getContentView()
and not itemView.
At runtime
4. Display headers with setDisplayHeadersAtStartUp(true)
.
5. Call setStickyHeaders(true)
, headers stay sticky if they are currently shown.
ℹ️ Note:
- You can still customize your
sticky_header_layout
by calling:setStickyHeaders(true, ViewGroup stickyContainer).
- You should call
getFlexibleAdapterPosition()
instead ofgetAdapterPosition()
only in Header ViewHolder, 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 now 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.
By applying custom Offset to the header item, it will be retained on Sticky Layout too:
mRecyclerView.addItemDecoration(new FlexibleItemDecoration(Context)
.addItemViewType(R.layout.header_item, 8, 8, 8, 8) //values in dpi
.withEdge(true));
You can read more about offsets on Flexible Item Decoration wiki page.
- To add new section:
- You can invoke
addSection(header)
. An empty section (the header) will be added to the top of the list; - You can invoke
addSection(header, comparator*)
. To add the section in the middle of the list with your custom sorting rules.
- You can invoke
- To remove a section:
- From 5.0.5, you can delete the whole section, header included, by invoking
removeSection(header);
.
- From 5.0.5, you can delete the whole section, header included, by invoking
- 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, 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)
.
*️⃣ = 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.
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 only when timeout is over (See UndoHelper wiki page).
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!
Check all details for expandable items.
Example 1 (preferable):
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!