Address:Github.com/stevenwsg/M…

1, the introduction

When I first saw MultiType, I was really amazed at how simple and decoupled a MultiType list could be. I had the pleasure of maintaining a ListView with 15 types of ViewHolder inserted into a single Adapter of more than 3,000 lines. While the ListView Adapter is easy to understand, focusing on getTypeCount(), getViewType, and getView(), it feels weird to write this, because if a young person on the project has never used ListView, Adding a new type and forgetting to increment the getTypeCount() method by 1 will result in an on-line accident and increase maintenance costs.

Recently, I realized that in daily business development, IF I feel uncomfortable or unreasonable, I would like to find a better solution. I think more, instead of moving bricks all the time. In terms of MultiType, the principle is basically understandable. It’s not a very deep thing. But people put forward this idea is relatively few, and after RecyclerVeiw emerged, from the Android 1.0 era, lists are realized by ListView, we realize multi-type lists are those three methods, Why didn’t a simpler solution come out (maybe some companies have big heads come out without open source), it’s worth thinking about.

Since last year, I have been trying to write about the ListView implementation of multi-type list implementation method, has been delayed until now, one reason is vegetables, one reason is lazy. I’ve been so busy with work. I perfected the idea while I wasn’t too busy.

RecyclerView MultiType list implementation — MultiType analysis, in simple terms, is to maintain a mapping table, the implementation of ViewType, Bean data, Mapping of ViewHolder.

Recall the implementation of MultiType:

Corresponding mapping table:

ViewType Java Bean Class Linker ItemViewBinder
0 C1 L1 binder_1
1 C2 L2 binder_2_1
2 C2 L2 binder_2_2
3 C2 L2 binder_2_3
4 C2 L3 binder_3_1

Implementation of MultiTypeListViewAdapter

Corresponding mapping table:

ViewType | Java Bean Class | ViewHolder —|—|—|— 0 | C0 | viewHolder0 1 | C1 | viewHolder1 2 | C2 | viewHolder2 3 | C3 | viewHolder3 4 | C4 | viewHolder4

MultiTypeListViewAdapter MultiTypeListViewAdapter MultiTypeListViewAdapter MultiTypeListViewAdapter MultiTypeListViewAdapter MultiTypeListViewAdapter MultiTypeListViewAdapter MultiTypeListViewAdapter MultiTypeListViewAdapter MultiTypeListViewAdapter

If you want to organize or refactor multiple types of ListViews in an old project, you can try this solution. The code changes and migrations are relatively small. Large scale refactoring may consider RecyclerView.

2, use,

MultiType is the same as MultiType

1. Create data entities

data class TextItem(val text : String)
Copy the code

2. Create ViewHolder

class TextViewHolder : BaseViewHolder<TextItem>() { private var text: TextView? = null override fun getLayoutId(): Int {return r.layout. item_text // Return VH layout file} Override fun onBindViewHolder(rootView: Override fun render(item: TextItem, position: Int) {text = rootView.findViewByid (r.idext) // bind View} Override fun render(item: TextItem, position: Int) {text? .text = item.text // Render data and set click events etc}}Copy the code

3. The Adapter binds the ViewHolder to inject data

class MainActivity : AppCompatActivity() { private var mList: MutableList<Any>? = null private var mListView: ListView? = null private var adapter: MultiTypeListViewAdapter? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initData(); initAdapter(); mListView = findViewById(R.id.listview) mListView? .adapter = adapter } private fun initData() { mList = ArrayList() for (i in 1.. 100) {// Add various types of data mList? .add(ImageItem(R.mipmap.ic_launcher)) mList? .add(RichItem(" ic_launcher_foreground ", r.drewable. Ic_launcher_foreground)) mList? .add(TextItem(" Nothing can stop you from being free ~"))}} // register ViewHolder private fun initAdapter() {// bind various types of VH and data, Adapter = MultiTypeListViewAdapter(this, mList) Adapter? .register(RichItem::class.java, RichTextViewHolder()) adapter? .register(ImageItem::class.java, ImageViewHolder()) adapter? .register(TextItem::class.java, TextViewHolder()) } }Copy the code

Implementation effect

3, the principle of

The idea is to encapsulate getTypeCount(), getViewType, and getView() to help developers avoid these three methods by maintaining the mapping table.

ViewType | Java Bean Class | ViewHolder —|—|—|— 0 | C0 | viewHolder0 1 | C1 | viewHolder1 2 | C2 | viewHolder2 3 | C3 | viewHolder3 4 | C4 | viewHolder4

How to map that, let’s start with the code: this is the project structure, the code is very small, if you are interested, you can download down to have a look, not only four classes

1, TypePool

interface TypePool { fun <T> register(clazz: Class<out T>, holder: BaseViewHolder<T>) fun unregister(clazz: Class<*>) fun size(): Int indexOf(clazz: Class<*>): Int positon fun getBaseViewHolder(index: Int): BaseViewHolder<*> }Copy the code

2, MultiTypePool

public class MultiTypePool : TypePool { private val classes: MutableList<Class<*>> private val holders: Class = ArrayList() {const const class = ArrayList()} constructor(initialCapacity: Int) {classes = ArrayList(initialCapacity) holders = ArrayList(initialCapacity)}  register( clazz: Class<out T>, holder: BaseViewHolder<T> ) { classes.add(clazz) holders.add(holder) } override fun unregister(clazz: Class<*>) { val postion = indexOf(clazz) classes.remove(clazz) holders.removeAt(postion) } override fun size(): Size} override fun indexOf(clazz: Class<*>): Int {// corresponding postion return class.indexof (clazz)} Override fun getBaseViewHolder(index: Int): BaseViewHolder<*> { return holders[index] } }Copy the code

3, BaseViewHolder

A ViewHolder is a normal ViewHolder. The following method is implemented conventionally.it can be seen that the MultiType is not used as a layer of proxy to reduce the cost of understanding

public abstract class BaseViewHolder<T> { protected var adapter: MultiTypeListViewAdapter? Abstract Fun getLayoutId(): Int abstract Fun onBindViewHolder(rootView: Render (item: T, position: Int)}Copy the code

4, MultiTypeListViewAdapter

public class MultiTypeListViewAdapter extends BaseAdapter { private static final String TAG = "MultiTypeListViewAdapter"; private List<? > items; private TypePool typePool; private LayoutInflater inflater; // The following methods, Public MultiTypeListViewAdapter(Context Context) {this(Context, collections.emptyList ()); } public MultiTypeListViewAdapter(Context context, List<? > items) { this(context, items, new MultiTypePool()); } public MultiTypeListViewAdapter(Context context, List<? > items, int initialCapacity) { this(context, items, new MultiTypePool(initialCapacity)); } public MultiTypeListViewAdapter(Context context, List<? > items, TypePool pool) { this.items = items; this.typePool = pool; inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void setItems(List<? > items) { this.items = items; } public List<? > getItems() { return items; } public <T> void register(Class<? extends T> clazz, BaseViewHolder<T> holder) { typePool.register(clazz, holder); holder.setAdapter(this); } @override public int getCount() {return items.size(); } @Override public Object getItem(int position) { return items.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { BaseViewHolder viewHolder; int viewType = getItemViewType(position); // if (convertView == null) { // viewHolder = typePool.getBaseViewHolder(viewType); // convertView = inflater.inflate(viewHolder.getLayoutId(), parent, false); // viewHolder.onBindViewHolder(convertView); // // convertView.setTag(viewHolder); // } else { // viewHolder = typePool.getBaseViewHolder(viewType); / /} / / VH reuse or have a problem with this, write down or find you see if there are any good way to solve the / / this is the way the VH don't reuse viewHolder = typePool. GetBaseViewHolder (viewType); // Get the corresponding ViewHolder convertView = inflater.inflate(viewholder.getLayoutid (), parent, false); / / initialize the viewHolder layout. OnBindViewHolder (convertView); //ViewHolder binding layout viewholder. render(getItem(position), position); //ViewHolder render data return convertView; } @override public int getViewTypeCount() {return typePool.size(); } @override public int getItemViewType(int position) {return getItemViewType(int position) typePool.indexOf(items.get(position).getClass()); }}Copy the code

The VH in getView() method is not reusable, so it is not recommended to do so, but the code written in the reusable version is not reusable.

4, summarize

So you’re probably not thinking about ListView anymore, you’re using RecyclerView. Although this article is about the ListView implementation of multi-type lists, but also some recent thoughts, share out. Interested students can download the source code to see.

GitHub address: github.com/stevenwsg/M…