MultiLevelAdapter is an Android library to allow collapsing and expanding items in RecyclerView's Adapter on multiple levels (no limits).
This library lets you collapse and expand items in your RecyclerView by only:
- Extending your adapter with abstract class
MultiLevelAdapter - Implementing the interface
MultiLevelItemor extending the abstract classAbstractMultiLevelItemin yourRecyclerView's item's class
dependencies {
implementation 'io.github.automatik:multileveladapter:1.0.1'
}- The interface
MultiLevelItem<R, T>lets you define both the type of the item's id and the item itself. - The abstract class
AbstractMultiLevelItem<R, T>lets you define both the type of the item's id and the item itself, but you don't need to implement all the interface's methods. - The interface
MultiLevelLongItem<T>is a shortcut and defines the id's type aslongand lets you define the item's class. - The abstract class
AbstractMultiLevelLongItem<T>is a shortcut and defines the id's type aslongand implements all the interface's methods, but lets you define the item's class. - The abstract class
MultiLevelAdapter<T, VH>already implements the logic behind collapsing and expanding items. You need only to call its listener when clicking collapse/expand. - The adapter's method
addItemadds an item to the adapter's list. If the item's parent is collapsed, the item will be added to its parent's children and will not be showed in theRecyclerView. - The adapter's method
addItemlets you add new items immediately, running on main UI thread, or delayed by using a Handler attached to main UI thread and adding theaddItemTaskto the message queue. This is to avoid possible heavy-blocking execution. In casedelayedistrueobviously the new item won't be immediately indexable in the list. - The adapter's method
addItemlets you avoid to add redundant items by checking the item's id.
Your item's class needs to have an id to uniquely identify the item in the list and to pass the parent's instance in the constructor. If the parent is null the item is considered as a top-level item (level = 1). The parent doesn't need to have all the fields complete but only the id field. The id field is used when adding the item to the list.
You don't need to manually edit or set the fields children, isCollapsed and level. They are used internally in the addItemTask.
The most straightforward way to use this library is by first extending your item's class (used in your list) with AbstractMultiLevelItem. The MyItem class has the id's type as long.
publicclassMyItemextendsAbstractMultiLevelItem<Long, MyItem> {
publicMyItem(Longid, MyItemparent) {
super(id, parent);
}
@Overridepublicbooleanequals(Objecto) {
if (this == o) returntrue;
if (o == null || getClass() != o.getClass()) returnfalse;
MyItemitem = (MyItem) o;
returngetId().equals(item.getId());
}
@OverridepublicinthashCode() {
returnObjects.hash(getId());
}
}And then extending your adapter's class with MultiLevelAdapter
publicclassAdapterextendsMultiLevelAdapter<MyItem, Adapter.ViewHolder> {
publicAdapter(List<MyItem> recyclerViewItems) {
super(recyclerViewItems);
}
@NonNull@OverridepublicViewHolderonCreateViewHolder(@NonNullViewGroupparent, intviewType) {
Viewview = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_item, parent, false);
//collapseItemListener is defined in MultiLevelAdapter classreturnnewAdapter.ViewHolder(view, getCollapseItemListener());
}
@OverridepublicvoidonBindViewHolder(@NonNullViewHolderholder, intposition, MyItemitem) {
holder.mItem = item;
if(item.isCollapsed())
holder.mCollapseText.setText("Expand");
elseholder.mCollapseText.setText("Collapse");
}
publicstaticclassViewHolderextendsMultiLevelViewHolder<MyItem> {
finalTextViewmCollapseText;
MyItemmItem;
publicViewHolder(@NonNullViewview, CollapseItemListener<MyItem> listener) {
super(view, listener);
mCollapseText = view.findViewById(R.id.item_expand_text);
mCollapseText.setOnClickListener(newView.OnClickListener() {
@OverridepublicvoidonClick(Viewv) {
if(mItem.isCollapsed()) {
listener.onExpand(mItem);
mCollapseText.setText("Collapse");
} else {
listener.onCollapse(mItem);
mCollapseText.setText("Expand");
}
}
});
}
}
}Finally, to add items to your list you need to call addItem in your Activity/Fragment
adapter.addItem(item);A sample app is also provided in this repository.
Instead of extending your item's class, you can implement the interface MultiLevelItem
publicclassMyItemimplementsMultiLevelItem<Long, MyItem> {
privateLongid;
privateMyItemparent;
privateList<MyItem> children = null;
privatebooleanisCollapsed = false;
privateintlevel = 0;
//The parent's instance is null so the item will be considered a top level item (level = 1).publicMyItem(longid) {
this.id = id;
parent = null;
}
publicMyItem(longid, MyItemparent) {
this.id = id;
this.parent = parent;
}
@OverridepublicvoidsetId(Longid) {
this.id = id;
}
@OverridepublicLonggetId() {
returnid;
}
@OverridepublicvoidsetIsCollapsed(booleanisCollapsed) {
this.isCollapsed = isCollapsed;
}
@OverridepublicbooleanisCollapsed() {
returnisCollapsed;
}
@OverridepublicvoidsetLevel(intlevel) {
this.level = level;
}
@OverridepublicintgetLevel() {
returnlevel;
}
@OverridepublicvoidsetParent(MyItemparent) {
this.parent = parent;
}
@OverridepublicMyItemgetParent() {
returnparent;
}
@OverridepublicbooleanhasChildren() {
returnchildren != null;
}
@OverridepublicvoidsetChildren(List<MyItem> children) {
this.children = children;
}
@OverridepublicList<MyItem> getChildren() {
returnchildren;
}
@Overridepublicbooleanequals(Objecto) {
if (this == o) returntrue;
if (o == null || getClass() != o.getClass()) returnfalse;
MyItemitem = (MyItem) o;
returngetId().equals(item.getId());
}
@OverridepublicinthashCode() {
returnObjects.hash(getId());
}
}- You can also define more hierarchies by implementing or extending the library's classes and interfaces and defining only one type.
- You can implement your own
TaskRunnerif you want to execute theaddItemTaskon another thread and pass it in the adapter's constructor. - Yoi can implement your own
AddItemTaskand override theaddItemmethod to define how you want to add your items to the adapter.
Copyright 2020 Emil Osterhed
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
