Top Related Projects
Epoxy is an Android library for building complex screens in a RecyclerView
"Favor composition over inheritance" for RecyclerView Adapters
The bullet proof, fast and easy to use adapter library, which minimizes developing time to a fraction...
An Adapter that allows a RecyclerView to be split into Sections with headers and/or footers. Each Section can have its state controlled individually.
Fast and versatile Adapter for RecyclerView which regroups several features into one library to considerably improve the user experience :-)
Migrated:
Quick Overview
MultiType is an Android library that simplifies the development of RecyclerViews with multiple item types. It provides a flexible and type-safe way to handle different view types in a single adapter, reducing boilerplate code and improving maintainability.
Pros
- Type-safe and flexible approach to handling multiple view types
- Reduces boilerplate code and simplifies adapter implementation
- Supports easy addition and removal of item types
- Promotes separation of concerns and modular design
Cons
- Learning curve for developers new to the concept
- May be overkill for simple RecyclerViews with few item types
- Requires careful management of item types to avoid conflicts
- Limited built-in support for DiffUtil (though can be implemented manually)
Code Examples
- Defining an ItemViewBinder:
class TextItemViewBinder : ItemViewBinder<TextItem, TextItemViewBinder.ViewHolder>() {
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
return ViewHolder(inflater.inflate(R.layout.item_text, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, item: TextItem) {
holder.textView.text = item.text
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.text)
}
}
- Registering item types in the adapter:
val adapter = MultiTypeAdapter()
adapter.register(TextItem::class.java, TextItemViewBinder())
adapter.register(ImageItem::class.java, ImageItemViewBinder())
- Setting up the RecyclerView:
val recyclerView: RecyclerView = findViewById(R.id.recycler_view)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
val items = listOf(TextItem("Hello"), ImageItem(R.drawable.image), TextItem("World"))
adapter.items = items
adapter.notifyDataSetChanged()
Getting Started
- Add the dependency to your
build.gradle
file:
dependencies {
implementation 'com.drakeet.multitype:multitype:4.3.0'
}
-
Create your item classes and corresponding
ItemViewBinder
classes. -
In your activity or fragment, set up the
MultiTypeAdapter
:
val adapter = MultiTypeAdapter()
adapter.register(YourItem::class.java, YourItemViewBinder())
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
// Add items to the adapter
adapter.items = yourItemsList
adapter.notifyDataSetChanged()
Competitor Comparisons
Epoxy is an Android library for building complex screens in a RecyclerView
Pros of Epoxy
- More comprehensive feature set, including view holders, data binding, and animations
- Better support for complex layouts and nested RecyclerViews
- Stronger type safety with generated models
Cons of Epoxy
- Steeper learning curve due to more complex API
- Requires annotation processing, which can increase build times
- Heavier dependency compared to MultiType's lightweight approach
Code Comparison
MultiType:
class TextItemViewBinder : ItemViewBinder<String, TextItemViewBinder.ViewHolder>() {
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
return ViewHolder(inflater.inflate(R.layout.item_text, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, item: String) {
holder.textView.text = item
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.text)
}
}
Epoxy:
@EpoxyModelClass(layout = R.layout.item_text)
abstract class TextItemModel : EpoxyModelWithHolder<TextItemModel.Holder>() {
@EpoxyAttribute lateinit var text: String
override fun bind(holder: Holder) {
holder.textView.text = text
}
class Holder : EpoxyHolder() {
lateinit var textView: TextView
override fun bindView(itemView: View) {
textView = itemView.findViewById(R.id.text)
}
}
}
"Favor composition over inheritance" for RecyclerView Adapters
Pros of AdapterDelegates
- More flexible delegation system, allowing for easier composition of complex adapters
- Better support for inheritance and subclassing of delegate types
- Cleaner separation of concerns between item types and their corresponding views
Cons of AdapterDelegates
- Slightly more verbose implementation, requiring more boilerplate code
- Less intuitive for simpler use cases with fewer item types
- Steeper learning curve for developers new to the delegate pattern
Code Comparison
MultiType:
class TextItemViewBinder : ItemViewBinder<TextItem, TextItemViewBinder.ViewHolder>() {
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
return ViewHolder(inflater.inflate(R.layout.item_text, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, item: TextItem) {
holder.textView.text = item.text
}
}
AdapterDelegates:
class TextAdapterDelegate : AdapterDelegate<List<Any>>() {
override fun isForViewType(items: List<Any>, position: Int): Boolean {
return items[position] is TextItem
}
override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder {
return TextViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false))
}
override fun onBindViewHolder(items: List<Any>, position: Int, holder: RecyclerView.ViewHolder, payloads: List<Any>) {
val textItem = items[position] as TextItem
(holder as TextViewHolder).textView.text = textItem.text
}
}
The bullet proof, fast and easy to use adapter library, which minimizes developing time to a fraction...
Pros of FastAdapter
- More comprehensive feature set, including drag & drop, swipe-to-dismiss, and expandable items
- Extensive documentation and sample apps for easier implementation
- Built-in support for different view types and layouts
Cons of FastAdapter
- Steeper learning curve due to its extensive API and features
- Potentially higher memory footprint for simple use cases
- More complex setup process compared to MultiType
Code Comparison
MultiType:
class TextItemViewBinder : ItemViewBinder<TextItem, TextItemViewBinder.ViewHolder>() {
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
return ViewHolder(inflater.inflate(R.layout.item_text, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, item: TextItem) {
holder.textView.text = item.text
}
}
FastAdapter:
class TextItem : AbstractItem<TextItem.ViewHolder>() {
override val layoutRes: Int = R.layout.item_text
override val type: Int = R.id.fastadapter_text_item
override fun getViewHolder(v: View): ViewHolder = ViewHolder(v)
class ViewHolder(view: View) : FastAdapter.ViewHolder<TextItem>(view) {
val textView: TextView = view.findViewById(R.id.text)
}
}
Both libraries offer efficient ways to handle multiple view types in RecyclerViews, but FastAdapter provides more built-in features at the cost of increased complexity. MultiType focuses on simplicity and flexibility, making it easier to use for basic scenarios.
An Adapter that allows a RecyclerView to be split into Sections with headers and/or footers. Each Section can have its state controlled individually.
Pros of SectionedRecyclerViewAdapter
- Built-in support for section headers and footers
- Easier management of complex, sectioned layouts
- Provides helper methods for section-specific operations
Cons of SectionedRecyclerViewAdapter
- More complex API, steeper learning curve
- Less flexible for non-sectioned layouts
- Potentially higher memory usage due to section management
Code Comparison
SectionedRecyclerViewAdapter:
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
sectionAdapter.addSection(new MySection());
recyclerView.setAdapter(sectionAdapter);
MultiType:
MultiTypeAdapter adapter = new MultiTypeAdapter();
adapter.register(Item.class, new ItemViewBinder());
recyclerView.setAdapter(adapter);
SectionedRecyclerViewAdapter is designed specifically for sectioned layouts, providing built-in support for headers and footers. It offers easier management of complex, sectioned data structures and includes helper methods for section-specific operations.
However, SectionedRecyclerViewAdapter has a more complex API and a steeper learning curve compared to MultiType. It's less flexible for non-sectioned layouts and may have higher memory usage due to its section management system.
MultiType, on the other hand, offers a simpler and more flexible approach for handling multiple view types. It's easier to use for general-purpose RecyclerView implementations but requires more manual work for implementing sectioned layouts.
The code comparison shows that SectionedRecyclerViewAdapter requires creating section objects, while MultiType uses a more straightforward registration of item types and binders.
Fast and versatile Adapter for RecyclerView which regroups several features into one library to considerably improve the user experience :-)
Pros of FlexibleAdapter
- More comprehensive feature set, including drag & drop, expandable items, and animations
- Built-in support for headers, footers, and section headers
- Extensive documentation and sample app showcasing various use cases
Cons of FlexibleAdapter
- Steeper learning curve due to its extensive feature set
- Larger library size, which may impact app size and performance
- More complex setup process compared to MultiType's minimalist approach
Code Comparison
MultiType:
class TextItemViewBinder : ItemViewBinder<TextItem, TextItemViewBinder.Holder>() {
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): Holder {
return Holder(inflater.inflate(R.layout.item_text, parent, false))
}
override fun onBindViewHolder(holder: Holder, item: TextItem) {
holder.text.text = item.text
}
class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val text: TextView = itemView.findViewById(R.id.text)
}
}
FlexibleAdapter:
class TextItem(val text: String) : AbstractFlexibleItem<TextItem.ViewHolder>() {
override fun bindViewHolder(adapter: FlexibleAdapter<*>, holder: ViewHolder, position: Int, payloads: List<*>) {
holder.text.text = text
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<*>): ViewHolder {
return ViewHolder(view, adapter)
}
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) {
val text: TextView = view.findViewById(R.id.text)
}
}
Migrated:
Pros of android-ui-toolkit-demos
- Comprehensive collection of UI toolkit demos
- Covers a wide range of Android UI components and patterns
- Official Google repository, ensuring high-quality and up-to-date examples
Cons of android-ui-toolkit-demos
- Not actively maintained (archived repository)
- Focuses on general UI components rather than specific list/recycler view optimization
- May contain outdated code examples due to its archived status
Code Comparison
MultiType:
class TextItemViewBinder : ItemViewBinder<TextItem, TextItemViewBinder.ViewHolder>() {
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
return ViewHolder(inflater.inflate(R.layout.item_text, parent, false))
}
// ... rest of the implementation
}
android-ui-toolkit-demos:
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private String[] mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(TextView v) {
super(v);
mTextView = v;
}
}
// ... rest of the implementation
}
Summary
MultiType focuses on simplifying RecyclerView adapters for multiple view types, while android-ui-toolkit-demos provides a broader range of UI examples. MultiType is actively maintained and offers a more modern approach to list management, whereas android-ui-toolkit-demos serves as a reference for various Android UI components but may contain outdated information.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
MultiType
Easier and more flexible to create multiple types for Android RecyclerView.
Previously, when we need to develop a complex RecyclerView / ListView, it is difficult and
troublesome work. We should override the getItemViewType()
of RecyclerView.Adapter
, add some types, and create some ViewHolder
s relating to those types. Once we need to add a new item type, we have to go to the original adapter file and modify some old codes carefully,
and these adapter classes will get more complicated.
Nowadays, I created a new intuitive and flexible way to easily create complex RecyclerViews, with the MultiType library, we could insert a new item type without changing any old adapter codes and make them more readable.
Getting started
In your build.gradle
:
MultiType has been rebuilt based on AndroidX. If you are still using the android support library, please use me.drakeet.multitype:multitype:3.4.4
and me.drakeet.multitype:multitype-kotlin:3.4.4
.
In addition, since 4.0.0 we have migrated to fully build with Kotlin. If you don't want to use Kotlin, you can use the last stable version me.drakeet.multitype:multitype:3.5.0
and see 3.x.
dependencies {
implementation 'com.drakeet.multitype:multitype:4.3.0'
}
Usage
Step 1. Create a Kotlin class
or data class
, for example:
data class Foo(
val value: String
)
Step 2. Create a class extends ItemViewDelegate<T, VH : ViewHolder>
, for example:
class FooViewDelegate: ItemViewDelegate<Foo, FooViewDelegate.ViewHolder>() {
override fun onCreateViewHolder(context: Context, parent: ViewGroup): ViewHolder {
// If you want a LayoutInflater parameter instead of a Context,
// you can use ItemViewBinder as the parent of this class.
return ViewHolder(FooView(context))
}
override fun onBindViewHolder(holder: ViewHolder, item: Foo) {
holder.fooView.text = item.value
Log.d("ItemViewDelegate API", "position: ${holder.bindingAdapterPosition}")
Log.d("ItemViewDelegate API", "items: $adapterItems")
Log.d("ItemViewDelegate API", "adapter: $adapter")
Log.d("More", "Context: ${holder.itemView.context}")
}
class ViewHolder(itemView : View): RecyclerView.ViewHolder(itemView) {
val fooView: TextView = itemView.findViewById(R.id.foo)
}
}
Or if you are using a custom View instead of XML layout, you can use ViewDelegate
:
The
ViewDelegate
is a simpleItemViewDelegate
that does not require to declare and provide aRecyclerView.ViewHolder
.
class FooViewDelegate : ViewDelegate<Foo, FooView>() {
override fun onCreateView(context: Context): FooView {
return FooView(context).apply { layoutParams = LayoutParams(MATCH_PARENT, WRAP_CONTENT) }
}
override fun onBindView(view: FooView, item: Foo) {
view.imageView.setImageResource(item.imageResId)
view.textView.text = item.text
view.textView.text = """
|${item.text}
|viewHolder: ${view.holder}
|layoutPosition: ${view.layoutPosition}
|absoluteAdapterPosition: ${view.absoluteAdapterPosition}
|bindingAdapterPosition: ${view.bindingAdapterPosition}
""".trimMargin()
}
}
(See RichViewDelegate
& RichView
examples for more details)
Step 3. register
your types and setup your RecyclerView
, for example:
class SampleActivity : AppCompatActivity() {
private val adapter = MultiTypeAdapter()
private val items = ArrayList<Any>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list)
val recyclerView = findViewById<RecyclerView>(R.id.list)
adapter.register(TextItemViewDelegate())
adapter.register(ImageItemViewDelegate())
adapter.register(RichItemViewDelegate())
recyclerView.adapter = adapter
val textItem = TextItem("world")
val imageItem = ImageItem(R.mipmap.ic_launcher)
val richItem = RichItem("å°è¾å¤§äººèµé«", R.drawable.img_11)
for (i in 0..19) {
items.add(textItem)
items.add(imageItem)
items.add(richItem)
}
adapter.items = items
adapter.notifyDataSetChanged()
}
}
That's all, you're good to go!
Advanced usage
One to many:
adapter.register(Data::class).to(
DataType1ViewDelegate(),
DataType2ViewDelegate()
).withKotlinClassLinker { _, data ->
when (data.type) {
Data.TYPE_2 -> DataType2ViewDelegate::class
else -> DataType1ViewDelegate::class
}
}
See OneDataToManyActivity
, OneToManyFlow
and OneToManyEndpoint
for more details.
More methods that you can override from ItemViewDelegate:
open fun onBindViewHolder(holder: VH, item: T, payloads: List<Any>)
open fun getItemId(item: T): Long
open fun onViewRecycled(holder: VH)
open fun onFailedToRecycleView(holder: VH): Boolean
open fun onViewAttachedToWindow(holder: VH)
open fun onViewDetachedFromWindow(holder: VH)
Android Studio Plugin
An intellij idea plugin for Android to generate MultiType
Item
and ItemViewDelegate
easily.
Screenshots
Pages created with MultiType:
License
Copyright (c) 2016-present. Drakeet Xu
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.
Top Related Projects
Epoxy is an Android library for building complex screens in a RecyclerView
"Favor composition over inheritance" for RecyclerView Adapters
The bullet proof, fast and easy to use adapter library, which minimizes developing time to a fraction...
An Adapter that allows a RecyclerView to be split into Sections with headers and/or footers. Each Section can have its state controlled individually.
Fast and versatile Adapter for RecyclerView which regroups several features into one library to considerably improve the user experience :-)
Migrated:
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot