XCoreRecyclerAdapter:一种通用的Adapter For RecyclerView
背景
每当我们使用RecyclerView写一个列表的时候,都需要写类似的如下代码:
...
mTestRv = (RecyclerView) view.findViewById(R.id.test_home_rv);
mTestRecyclerAdapter = new TestRecyclerAdapter(context);
mLinearLayoutManager = new LinearLayoutManager(context);
mTestRv.setLayoutManager(mLinearLayoutManager);
mTestRv.setAdapter(mTestRecyclerAdapter);
...
那么,我们一般必不可少的是写一个Adapter,比如,为了支持多种类型,我们需要重写getItemViewType、onCreateViewHolder、onBindViewHolder等方法。比如:
public class TestRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> {
...
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
switch (viewType) {
case Type.TYPE_HEADER:
view = LayoutInflater.from(mContext).inflate(R.layout.card_header,
parent, false);
return new HeaderViewHolder(view);
case Type.TYPE_HIS_HEAD:
view = LayoutInflater.from(mContext).inflate(R.layout.his_header,
parent, false);
return new HeaderHisViewHolder(view);
...
default:
break;
}
return new HeaderViewHolder(new RelativeLayout(mContext));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case Type.TYPE_HEADER:
HeaderViewHolder headerViewHolder = (HeaderViewHolder) holder;
headerViewHolder.bindView(mDataSet.get(position));
break;
case Type.TYPE_HIS_HEAD:
HeaderHisViewHolder headerHisViewHolder = (HeaderHisViewHolder) holder;
headerHisViewHolder.bindView(mDataSet.get(position), mOnHeaderItemClickListener);
break;
...
default:
break;
}
}
@Override
public int getItemViewType(int position) {
Data data = mDateSet.get(position);
String type = data.getType();
if("xxx".equals(type)){
return Type.TYPE_HEADER;
}else if("xxxxx".equals(type)){
return Type.TYPE_HIS_HEAD;
}else if(){
...
}
...
}
}
按照上述写法,每当我们需要增加一种类型的时候,都需要修改TestRecyclerAdapter的代码,在onCreateViewHolder等方法中添加对应的代码。后来大家觉得这个挺烦的,然后就把onCreateViewHolder中的switch语句放到工厂类当中,这一定层度上缓解了修改Adapter的频率。那么,我们能不能写一个相对通用的Adapter呢?
封装通用的插件式Adapter
我们希望做到如下目标:
- 1).Adapter通用化,无需每次新建Adapter
- 2).Item(Cell)的组件是插件式的,解耦,并可复用
用代码描述为:
...
//创建通用的Adapter
mXCoreRecyclerAdapter = new XCoreRecyclerAdapter(context);
//Adapter注册item组件
mXCoreRecyclerAdapter.registerItemUIComponent(new TodoItemComponent())
.registerItemUIComponent(new TestItemComponent());
//设置数据源,完成展示
mXCoreRecyclerAdapter.setDataSet(List<IDataComponent> dataSet)
...
插件式的通用XCoreRecyclerAdapter的原理图如下:
每当我们需要添加一个新的类型时,只需要一下几步:
-
1.新建一个TodoItemUIComponent组件,继承自XCoreItemUIComponent
实现对应的方法;
-
2.注册新增的组件TodoItemUIComponent
调用XCoreRecyclerAdapter的registerItemUIComponent方法即可注册。
- 3.数据源实现IDataComponent接口
getViewType和组件TodoItemUIComponent的getViewType匹配即可;
这样,每次新增一种新的类型的Item时,无需修改Adapter的代码。并且,Item的组件是可复用的。
那么具体怎么设计的呢?
数据源
首先对数据源进行抽象:在Adapter列表中,需要根据数据源Data的不同类型,选择不同的ViewHolder。所以,数据源必须有一个getType的方法。
/**
* 数据源必须实现的接口
*/
public interface IDataComponent {
String getViewType();
}
Item组件 ItemUIComponent
ItemUIComponent组件负责某一类type的管理。为了做到插件式,它必须实现的方法有:
- onCreateView方法
该方法是用于生成Item的根View的。当Adapter调用onCreateViewHolder时,该方法会被调用。注意,子View的初始化,不要写在onCreateView中。需要写在onViewCreated方法中。 - onViewCreated方法
创建完ItemComponent之后,就会立即回调该方法,View的初始化请写在这里。 - getViewType方法
该方法是为了和数据源进行关联的,数据源的getViewType方法和组件的getViewType方法的值一致时,即完成匹配。 - bindView方法
该方法会在Adapter回调onBindViewHolder
public class TodoItemComponent extends XCoreItemUIComponent {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container) {
//TODO 在这里指定item组件的布局
return inflater.inflate(R.layout.todo_item_layout,container,false);
}
@Override
public void onViewCreated(View view) {
//TODO 在这里写View初始化
view.findViewById();
...
}
@Override
public String getViewType() {
//与数据源的getViewType关联
return TodoItemComponent.class.getSimpleName();
}
@Override
public void bindView(IXCoreComponent coreComponent,
XCoreRecyclerAdapter coreRecyclerAdapter,
XCoreRecyclerAdapter.IDataComponent data,
int pos) {
//TODO 在这里写绑定逻辑
}
}
数据源和Item组件都已经定义好,那么,核心的XCoreRecyclerAdapter源码应该怎么做呢?
XCoreRecyclerAdapter源码
package com.github.nuptboyzhb.xcore.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.github.nuptboyzhb.xcore.components.IXCoreComponent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @version mochuan.zhb on 2016/8/9.
* @Author Zheng Haibo
* @Blog github.com/nuptboyzhb
* @Company Alibaba Group
* @Description 通用的Adapter for RecyclerView
*/
public class XCoreRecyclerAdapter extends RecyclerView.Adapter<XCoreRecyclerAdapter.CommonViewHolder> {
private IXCoreComponent mIXCoreComponent;//外层UI组件
private List<IDataComponent> mDataSet = new ArrayList<IDataComponent>();//数据源
private SparseArray<XCoreItemUIComponent> mConfigurationSparseArray = new SparseArray<XCoreItemUIComponent>();//集合:type对应的Item组件
private Map<String, Integer> mViewTypeMap = new HashMap<String, Integer>();//type的string和int映射
public XCoreRecyclerAdapter() {
}
public XCoreRecyclerAdapter(IXCoreComponent mIXCoreComponent) {
this.mIXCoreComponent = mIXCoreComponent;
}
@Override
public CommonViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
//根据数据类型获取对应的item组件
XCoreItemUIComponent XCoreItemUIComponent = mConfigurationSparseArray.get(type);
if (XCoreItemUIComponent == null) {//如果未获取到,展示空item
return getDefaultViewHolder(viewGroup.getContext());
}
try {
//使用item组件创建一个新的View
View view = XCoreItemUIComponent.onCreateView(LayoutInflater.from(viewGroup.getContext()), viewGroup);
//使用View构建内部的ViewHolder
CommonViewHolder commonViewHolder = new CommonViewHolder(view);
//创建一个新的Item组件
XCoreItemUIComponent realItem = XCoreItemUIComponent.getClass().newInstance();
//将创建的View设置到真是的Item组件中
realItem.setItemView(view);
//使用内部ViewHolder
commonViewHolder.setXCoreItemUIComponent(realItem);
return commonViewHolder;
} catch (Throwable t) {
t.printStackTrace();
}
return getDefaultViewHolder(viewGroup.getContext());
}
@Override
public void onBindViewHolder(CommonViewHolder baseViewHolder, int pos) {
baseViewHolder.bindView(mIXCoreComponent, this, mDataSet.get(pos), pos);
}
@Override
public int getItemViewType(int position) {
IDataComponent item = mDataSet.get(position);
Integer integer = mViewTypeMap.get(item.getViewType());
if (integer == null) {
return -1;
}
return integer;
}
@Override
public int getItemCount() {
if (mDataSet != null) {
return mDataSet.size();
}
return 0;
}
/**
* 获取Adapter的数据源
*
* @return
*/
public List<IDataComponent> getDataSet() {
return mDataSet;
}
/**
* 设置Adapter的数据源
*
* @param dataSet
*/
public void setDataSet(List<IDataComponent> dataSet) {
this.mDataSet = dataSet;
notifyDataSetChanged();
}
/**
* get the error view holder
*
* @param context
* @return
*/
protected CommonViewHolder getDefaultViewHolder(Context context) {
return new CommonViewHolder(new View(context));
}
/**
* get the unique int type
*
* @param name
* @return
*/
private int getUniqueIntType(String name) {
if (TextUtils.isEmpty(name)) {
return -1;
}
int type = name.hashCode();
while (true) {
XCoreItemUIComponent old = mConfigurationSparseArray.get(type);
if (old != null) {
String oldName = old.getViewType();
if (!name.equals(oldName)) {
type = type + 1;
} else {
return type;
}
} else {
return type;
}
}
}
/**
* 注册Item组件
*
* @param XCoreItemUIComponent
* @return
*/
public XCoreRecyclerAdapter registerItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) {
if (XCoreItemUIComponent == null || TextUtils.isEmpty(XCoreItemUIComponent.getViewType())) {
return this;
}
int viewTypeInt = getUniqueIntType(XCoreItemUIComponent.getViewType());
mViewTypeMap.put(XCoreItemUIComponent.getViewType(), viewTypeInt);
mConfigurationSparseArray.put(viewTypeInt, XCoreItemUIComponent);
return this;
}
/**
* 注销配置
*
* @param XCoreItemUIComponent
* @return
*/
public XCoreRecyclerAdapter unregisterItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) {
if (XCoreItemUIComponent == null || TextUtils.isEmpty(XCoreItemUIComponent.getViewType())) {
return this;
}
int index = mConfigurationSparseArray.indexOfValue(XCoreItemUIComponent);
if (index == -1) {
return this;
}
mConfigurationSparseArray.remove(index);
return this;
}
/**
* 数据源必须实现的接口
*/
public interface IDataComponent {
String getViewType();
}
/**
* 使用CommonViewHolder代理XCoreItemUIComponent组件
*/
public static class CommonViewHolder extends RecyclerView.ViewHolder {
private XCoreItemUIComponent XCoreItemUIComponent;
public void setXCoreItemUIComponent(XCoreItemUIComponent XCoreItemUIComponent) {
this.XCoreItemUIComponent = XCoreItemUIComponent;
}
public XCoreItemUIComponent getXCoreItemUIComponent() {
return XCoreItemUIComponent;
}
public CommonViewHolder(View itemView) {
super(itemView);
}
public void bindView(IXCoreComponent mIXCoreComponent,
XCoreRecyclerAdapter XCoreRecyclerAdapter,
XCoreRecyclerAdapter.IDataComponent data,
int pos) {
if (XCoreItemUIComponent == null) {
return;
}
XCoreItemUIComponent.bindView(mIXCoreComponent, XCoreRecyclerAdapter, data
, pos);
}
public void onViewDetachedFromWindow() {
if (XCoreItemUIComponent != null) {
XCoreItemUIComponent.onViewDetachedFromWindow();
}
}
}
@Override
public void onViewDetachedFromWindow(CommonViewHolder holder) {
super.onViewDetachedFromWindow(holder);
holder.onViewDetachedFromWindow();
}
}
XCoreItemUIComponent源码
package com.github.nuptboyzhb.xcore.adapter;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.github.nuptboyzhb.xcore.components.IXCoreComponent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @version mochuan.zhb on 2016/8/12.
* @Author Zheng Haibo
* @Blog github.com/nuptboyzhb
* @Company Alibaba Group
* @Description item component 抽象类
*/
public abstract class XCoreItemUIComponent implements IXCoreComponent {
/**
* 创建View
*
* @param inflater
* @param container
* @return
*/
public abstract View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container);
/**
* 在此做View的初始化操作
*
* @param view
*/
public abstract void onViewCreated(View view);
/**
* item组件支持的ViewType,与IDataComponent的getViewType对应
*
* @return
*/
public abstract String getViewType();
/**
* 绑定数据-频繁回调
*
* @param coreComponent 外出UI组件
* @param coreRecyclerAdapter Adapter
* @param data 对应的数据源
* @param pos item所在列表的位置
*/
public abstract void bindView(IXCoreComponent coreComponent,
XCoreRecyclerAdapter coreRecyclerAdapter,
XCoreRecyclerAdapter.IDataComponent data,
int pos);
/**
* item组件销毁时调用
*/
public abstract void onViewDetachedFromWindow();
//必须有无参数的构造函数
public View itemView;
public void setItemView(View itemView) {
this.itemView = itemView;
onViewCreated(itemView);
}
}
后续
本篇博客主要是介绍通用的XCoreRecyclerAdapter引擎,极大提高了Adapter的复用率,让开发集中把精力集中在必要的业务代码上,提高开发效率。另外,与XCoreRecyclerAdapter引擎相配套的还有UI组件化、数据控制框架XCoreRedux、数据绑定等。请参考下一遍博客:《Android Redux实践与UI组件化:XCoreRedux框架》(ing...)