The CompositeAdapter extends ListAdapter and delegates all UI logic to a Cell<DATA, VIEW_HOLDER> created by the ViewModel.
Cells handles all work with ViewHolders, DiffUtils and ItemDecorations.
repositories {
mavenCentral()
}
implementation("io.github.netcosports.compositeadapter:composite-adapter:${compositeAdapterVersion}")
| Sample | Description |
|---|---|
| Basic | A simple setup how to create and show Cell inside CompositeAdapter |
| Decorations | Using a different ItemDecoration for each Cell |
| Different bindings | Using ViewBinding/DataBinding/CustomViews |
| Inner RecyclerView/Webview/VideoPlayer or other complex view |
Handle binding for complex views via payload and save the scroll state of inner RecyclerViews |
| State as Cells | Show all requests (including Loading/Error/Empty/Data states and SwipeRefresh/Reload/Error actions) in a single RecyclerView without any additional Views |
1. Implement SampleCell:
data class SampleCell(
override val data: SampleUI, // Must be kotlin data class or with correct equals
override val decoration: ItemDecoration? = null, // ItemDecoration for this SampleCell instance only
override val onClickListener: ((GenericClickItem<SampleUI>) -> Unit)? = null // Root View.OnClickListener
) : Cell<SampleUI, SampleCell.SampleViewHolder> {
override val uniqueId: String = data.id // Must be unique for this viewType
override val viewType: Int = R.layout.sample_cell // Can be generated via ids.xml
override fun onCreateViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
viewType: Int
): SampleViewHolder {
return SampleViewHolder(SampleCellBinding.inflate(inflater, parent, false))
}
override fun onBindViewHolder(holder: SampleViewHolder, position: Int) {
holder.binding.text.text = data.name
}
class SampleViewHolder(
val binding: SampleCellBinding
) : RecyclerView.ViewHolder(binding.root)
}
// You don't need to list the supported ViewTypes and ViewHolders for the CompositeAdapter.
val adapter = CompositeAdapter()
recyclerView.adapter = adapter
recyclerView.layoutManager = TODO("layoutManager")
// Each Cell can have their own ItemDecoration.
// To activate this functionality, don't forget to register CompositeItemDecoration.
recyclerView.addItemDecoration(CompositeItemDecoration())
val items: List<GenericCell> = listOf(SampleCell(...), HorizontallStoriesCell(...), TitleCell(...), NewsCell(...), ...) adapter.submitList(items)
That's all.
Forget about androidx.recyclerview.widget.ItemDecoration and use com.originsdigital.compositeadapter.decoration.ItemDecoration with the additional parameter Cell instead.
Each Cell can have their own ItemDecoration that only affects them.
In most cases we don't need a separate ViewHolder for each Cell, so we can remove this copy-paste, for example with the base implementation of Cell.
Forget about ViewHolders for each viewType, you don't need it anymore. Instead, implement a simple and consistent ViewHolder, for example:
class ViewBindingViewHolder<VIEW_BINDING : ViewBinding>(
val binding: VIEW_BINDING
) : ViewHolder(binding.root)
class DataBindingViewHolder<DATA_BINDING : ViewDataBinding>(
val binding: DATA_BINDING
) : ViewHolder(binding.root) {
companion object {
fun <DATA_BINDING : ViewDataBinding> create(
inflater: LayoutInflater,
layoutResId: Int,
parent: ViewGroup
): DataBindingViewHolder<DATA_BINDING> {
return DataBindingViewHolder(
DataBindingUtil.inflate(
inflater,
layoutResId,
parent,
false
) as DATA_BINDING
)
}
}
}
and use this ViewHolder in all Cells.
If you are using ViewBinding or DataBinding, its good idea to have a base Cell with default implementation of onCreateViewHolder and onBindViewHolder(the last one is for DataBinding only).
abstract class ViewBindingCell<DATA, VIEW_BINDING : ViewBinding>
: Cell<DATA, ViewBindingViewHolder<VIEW_BINDING>> {
abstract fun createViewBinding(
inflater: LayoutInflater,
parent: ViewGroup,
viewType: Int
): VIEW_BINDING
final override fun onCreateViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
viewType: Int
): ViewBindingViewHolder<VIEW_BINDING> {
return ViewBindingViewHolder(createViewBinding(inflater, parent, viewType))
}
}
abstract class DataBindingCell<DATA, DATA_BINDING : ViewDataBinding>
: Cell<DATA, DataBindingViewHolder<DATA_BINDING>> {
@get:LayoutRes
abstract val layoutId: Int
override val viewType: Int get() = layoutId
final override fun onCreateViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
viewType: Int
): DataBindingViewHolder<DATA_BINDING> {
return DataBindingViewHolder.create(inflater, layoutId, parent)
}
override fun onBindViewHolder(holder: DataBindingViewHolder<DATA_BINDING>, position: Int) {
(holder.binding).apply {
setVariable(BR.item, data)
executePendingBindings()
}
}
}
Now you don't need to copy and paste this code into every Cell.
data class SampleCell(
override val data: SampleEntity, // Must be kotlin data class or with correct equals
override val decoration: ItemDecoration? = null, // ItemDecoration for this SampleCell instance only
override val onClickListener: ((GenericClickItem<SampleEntity>) -> Unit)? = null // Root View.OnClickListener
) : ViewBindingCell<SampleEntity, SampleCellBinding>() {
override val uniqueId: String = data.id // Must be unique for this viewType
override val viewType: Int = R.layout.sample_cell // Can be generated via ids.xml
override fun createViewBinding(
inflater: LayoutInflater,
parent: ViewGroup,
viewType: Int
): SampleCellBinding {
return SampleCellBinding.inflate(inflater, parent, false)
}
override fun onBindViewHolder(
holder: ViewBindingViewHolder<SampleCellBinding>,
position: Int
) {
holder.binding.text.text = data.text
}
}
data class SampleCell(
override val data: SampleEntity, // Must be kotlin data class or with correct equals
override val decoration: ItemDecoration? = null, // ItemDecoration for this SampleCell instance only
override val onClickListener: ((GenericClickItem<SampleEntity>) -> Unit)? = null // Root View.OnClickListener
) : DataBindingCell<SampleEntity, SampleCellBinding>() {
override val uniqueId: String = data.id // Must be unique for this viewType (by default viewType == layoutId)
override val layoutId: Int = R.layout.sample_cell // Can be generated via ids.xml (by default viewType == layoutId)
// If you have all bindings inside the R.layout.sample_cell,
// you don't need to override onBindViewHolder because everything is done inside the DataBindingCell
// But you can move all bindings from layout to onBindViewHolder
// override fun onBindViewHolder(
// holder: DataBindingViewHolder<SampleCellBinding>,
// position: Int
// ) {
// super.onBindViewHolder(holder, position)
// }
}
Cell has all methods related to ViewHolder, DiffUtil.ItemCallback and ItemDecoration, the full list is here.