通常实现一个Adapter需要有两步:
- 继承RecyclerView.Adapter定义Adapter
- 继承RecyclerView.ViewHolder定义视图容器
可以看到实现不同Adapter会造成ViewHolder冗余,这是第一个可以简化的点。
Adapter提供的视图,需要提供点击事件监听,但是在Adapter的上下文中并不充分,这就不得不传入更多的上下文内容。从Adapter的设计上来说,主要功能就是提供视图,传入更多冗杂的内容显然是不合适的,这是第二个可以简化的点。
Adapter支持多种视图,但是在视图类型比较多的时候,Adapter会变得很庞大和难以维护,这是第三个可以简化的点。
接下来将介绍简化思路和关键代码,文末连接有详细代码。
通用ViewHolder
创建一个通用ViewHolder来解决第一个点。
不同ViewHolder有不同的视图,怎么实现通用?
通常ViewHolder内部持有的都是各种View子类,这里改为持有ViewDataBinding子类。
再借助泛型就可以实现通用ViewHolder了。
public class RVViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
public T binding;
public RVViewHolder(@NonNull View itemView) {
super(itemView);
binding=DataBindingUtil.bind(itemView);
}
}
暴露点击事件接口
暴露点击事件接口,便能够是事件监听提升至Adapter范围外(比如Activity),可以简化第二个点。
定义一个点击事件接口:
public interface RVIClickListener {
void onClick(View view, int position, int viewType);
}
定义一个Adapter基类,重写onCreateViewHolder(ViewGroup,int)方法:
public RVViewHolder<R> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
RVViewHolder<R> vh = new RVViewHolder<R>(inflater.inflate(getItemLayout(), parent, false));
if (itemClickListener != null) {
vh.itemView.setOnClickListener((view) -> {
itemClickListener.onClick(view, vh.getLayoutPosition(), vh.getItemViewType());
});
}
for (int i = 0; i < viewClickListeners.size(); i++) {
int key = viewClickListeners.keyAt(i);
View view = vh.itemView.findViewById(key);
if (view != null) {
view.setOnClickListener((v) -> {
viewClickListeners.get(key).onClick(v, vh.getLayoutPosition(), vh.getItemViewType());
});
}
}
return vh;
}
Adapter支持单一类型
既然这是由于Adapter支持多视图类型引起,那直接让Adapter只支持一种视图。是的,你没看错!
在上面的onCreateViewHolder(ViewGroup,int)方法中,使用getItemLayout()获取视图ID,每个Adapter都需要重写这个方法。
public abstract int getItemLayout();
单一视图解决了,但是多类型视图怎么办?
升级到RecyclerView1.2.0以上的版本,可以使用ConcatAdapter代理多种类型Adapter。
RVAdApter
RVAdapter轮子已经造好了,enjoy it!