MultiTypeAdapter
一款轻量级支持多数据类型的 RecyclerView 适配器; 使用简单,完全解耦;
通讯聊天界面、朋友圈布局、淘宝 UI等复杂页面 优雅快速实现,无论你是一种数据有多种VIew类型,还是多种数据多种类型,还是两者都有,统统帮你快速地、优雅地搞定!
好了,话不多说,直接开始撸代码吧 (代码为Java + Kotlin混合开发)。
implementation 'me.drakeet.multitype:multitype:3.3.0'
虽然 MultiTypeAdapter 中已经有实现方式了,但是我对其进行进一步优化封装。使用起来更加快捷简单。
首先,对ViewHolder进行封装:
ViewHolder.kt
import android.content.Context
import android.util.SparseArray
import android.view.View
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
@Suppress("NOTHING_TO_INLINE")
open class ViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val cachedViews: SparseArray<View> by lazy { SparseArray<View>() }
open var item: T? = null
open val context: Context
get() = itemView.context
@Suppress("UNCHECKED_CAST")
operator fun <T : View> get(@IdRes id: Int): T = getOptional(id)!!
fun <T : View> getOptional(@IdRes id: Int): T? {
val cachedView = cachedViews[id]
return if (cachedView != null) {
cachedView as T
} else {
val v = itemView.findViewById<View?>(id)
if (v != null) {
cachedViews.put(id, v)
}
v as T?
}
}
fun <T : View> findViewById(@IdRes id: Int): T = get(id)
fun getString(@StringRes id: Int, vararg args: Any?): String =
context.getString(id, *args)
}
ViewHoder封装完成后,下一步就是封装 ItemViewBinder,
BaseLayoutBinder.kt
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import me.drakeet.multitype.ItemViewBinder
import java.lang.reflect.Executable
import java.util.concurrent.Executor
abstract class BaseViewHolderLayoutBinder<T, VH : ViewHolder<T>>
: ItemViewBinder<T, VH>() {
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): VH {
val holder = onCreateViewHolderImpl(inflater, parent)
// 检查是否是执行在ui线程
if (Looper.myLooper() == Looper.getMainLooper()) {
onViewHolderCreated(holder)
} else {
Handler(Looper.getMainLooper()).post {
Runnable() {
kotlin.run {
onViewHolderCreated(holder)
}
}
}
}
return holder
}
abstract fun onCreateViewHolderImpl(inflater: LayoutInflater, parent: ViewGroup): VH
protected open fun onViewHolderCreated(holder: VH) {}
override fun onBindViewHolder(holder: VH, item: T, payloads: List<Any>) {
holder.item = item
super.onBindViewHolder(holder, item, payloads)
}
}
abstract class BaseLayoutBinder<T>(
@LayoutRes private val layout: Int
) : BaseViewHolderLayoutBinder<T, ViewHolder<T>>() {
override fun onCreateViewHolderImpl(inflater: LayoutInflater, parent: ViewGroup): ViewHolder<T> {
return ViewHolder(inflater.inflate(layout, parent, false))
}
}
如此,工具类就算添加完成了。两个工具类都是使用Kotlin实现的。如果不懂Kotlin的兄弟们就自己补习吧。
3.1、 按照结构。首先添加一个Model类,用来保存数据, 这个没有什么好说的:
Model.java
public class Model {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3.2、 其次,需要新建关于RecycleView的Item布局:
item_binder.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/item"
android:layout_width="match_parent"
android:text="sss"
android:textSize="20sp"
android:gravity="center"
android:layout_height="40dp"/>
LinearLayout>
3.3、 然后添加Binder类,也就是Adapter类, 使Binder类继承 BaseLayoutBinder并实现onBindViewHolder:
ItemMainBinder.kt
import android.widget.TextView
abstract class ItemMainBinder : BaseLayoutBinder<Model>(R.layout.binder_item) {
override fun onViewHolderCreated(holder: ViewHolder<Model>) {
super.onViewHolderCreated(holder)
holder.run {
//添加item点击事件
holder.itemView.setOnClickListener {
holder.item?.let {
itemClick(it)
}
}
}
}
//控制器,初始控件并绑定数据
override fun onBindViewHolder(hoder: ViewHolder<Model>, item: Model) {
hoder.run {
get<TextView>(R.id.item).text = item.name
}
}
abstract fun itemClick(item: Model)
}
3.4、 最后就是关于在Activity中的使用方法:
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import me.drakeet.multitype.MultiTypeAdapter;
public class MainActivity extends AppCompatActivity {
private List<Model> ms = new ArrayList<>();
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = this.findViewById(R.id.recyclerView);
Model model = new Model();
for (int i = 0; i <= 20; i++) {
model.setName("测试" + i);
model.setAge(i);
ms.add(model);
}
// 初始化MultiTypeAdapter
MultiTypeAdapter adapter = new MultiTypeAdapter(ms);
//注册binder
adapter.register(Model.class)
.to(new ItemMainBinder() {
//下标为0的binder
@Override
public void itemClick(@NotNull Model item) {
}
}, new MainTitleBinder() {
//下标为1的binder
@Override
public void onItemTitleClick(@NotNull Model item) {
}
})
.withLinker(new Linker<Model>() {
@Override
public int index(@NonNull Model model) {
//此处return返回的即是加载对应下标的布局
if (model.getAge() == 3 || model.getAge() == 10) {
return 1;
} else {
return 0;
}
}
});
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
}
以上是单一布局的实现方式,如果为单个Model且多布局实现,可利用MultiTypeAdapter的 to 方法加载多个binder。再实现 withLinker 方法,用于根据id之类区分加载某个binder。返回id值和binder初始化顺序相对应。如下方式:
// 初始化MultiTypeAdapter
MultiTypeAdapter adapter = new MultiTypeAdapter(models);
//注册binder
adapter.register(Model.class)
.to(new ItemMainBinder() {
//下标为0的binder
@Override
public void itemClick(@NotNull Model item) {
}
}, new MainTitleBinder() {
//下标为1的binder
@Override
public void onItemTitleClick(@NotNull Model item) {
}
})
.withLinker(new Linker<Model>() {
@Override
public int index(@NonNull Model model) {
//此处return返回的即是加载对应下标的布局
if (model.getAge() == 3 || model.getAge() == 10) {
return 1;
} else {
return 0;
}
}
});
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
到此,MultiTypeAdapter的使用及封装就完结了。欢迎提出宝贵意见。