超级简单RecycleViewAdapter的封装-支持多视图,
其实RecycleView已经出来很长时间了,对RecycleView的用法网上也有很多教程了。本篇文章不讲解RecycleView的用法,不讲解LayoutManager的用法也不讲解ItemDecoration的用法,我们只关注Adapter的用法以及如何封装成一个通用的Adapter
Adapter的正常使用方法
其实很简单,只需要继承RecyclerView.Adapter<VH extends ViewHolder>传入范型类型为ViewHolder的子类就可以,代码演示如下:
MainActivity.java
public class MainActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
List<String> datas = new ArrayList<>();
for (int i = 0; i < 20; ++i) {
datas.add("item:" + (i + 1));
}
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
MyAdapter myAdapter = new MyAdapter(datas);
myAdapter.setItemClickListener(new MyAdapter.ItemClickListener() {
@Override
public void onItemClicked(View view, int position) {
Log.d(TAG,"root clicked..." + position);
}
});
recyclerView.setAdapter(myAdapter);
}
private static class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private List<String> dataList;
private ItemClickListener itemClickListener;
public MyAdapter(List<String> dataList){
this.dataList = dataList;
}
public interface ItemClickListener {
void onItemClicked(View view,int position);
}
//设置点击回调接口
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
//生成ViewHolder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view_main1, parent, false);
return new ViewHolder(itemView);
}
private String getItem(int position){
return dataList.get(position);
}
//更新列表Item视图(根据需要绑定click事件)
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
String str = getItem(position);
// holder.icon.setImageDrawable(xxx);
holder.name.setText(str);
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(itemClickListener != null)
itemClickListener.onItemClicked(v,position);
}
});
}
@Override
public int getItemCount() {
return dataList.size();
}
//ViewHolder保存每个item视图
public class ViewHolder extends RecyclerView.ViewHolder{
private ImageView icon;
private TextView name;
private View root;
public ViewHolder(View itemView) {
super(itemView);
icon = (ImageView)itemView.findViewById(R.id.icon);
name = (TextView)itemView.findViewById(R.id.id_text);
root = itemView.findViewById(R.id.root);
}
}
}
}
通过以上代码演示,我们可以得出结论:构造一个比较完整的Adapter至少需要完成以下三件事情
-
onCreateViewHolder
通过视图Id加载不同Item视图并生成ViewHolder用来保存每个列表Item视图 -
onBindViewHolder
更新列表Item视图(填充model数据) - 新建ViewHolder类来存储Item视图及其子视图
如果需要实现点击事件,需要在onBindViewHolder中适当绑定点击事件,比如在以上代码中绑定了点击列表视图中的Item根视图的点击事件如下:
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(itemClickListener != null)
itemClickListener.onItemClicked(v,position);
}
});
在需要实现具体的点击事件时调用如下代码:
myAdapter.setItemClickListener(new MyAdapter.ItemClickListener() {
@Override
public void onItemClicked(View view, int position) {
Log.d(TAG,"root clicked..." + position);
}
});
问题
- 如果需要实现在同一个Item视图中点击不同view要实现不同功能
比如以上代码中处理了点击Item跟视图的点击事件,如果需要点击根视图中的icon子视图如何实现?
是不是继续写setOnXXXListener,麻烦也不太现实 - 如果要实现列表中多视图展示如何实现?正常如果需要支持多视图咱们是在
onCreateViewHolder
方法中根据不同的ViewType来加载不同的Item视图代码演示如下:
//重写getItemViewType函数,更具需要返回不同的viewType
@Override
public int getItemViewType(int position) {
String model = getItem();
if(...){
return 1;
}else if(...){
return 2;
}
return super.getItemViewType(position);
}
//生成ViewHolder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int itemViewId = -1;
if(viewType == 1){
itemViewId = R.layout.item_view_main1;
}else if(viewType == 2){
itemViewId = R.layout.item_view_main2;
}
View itemView = LayoutInflater.from(parent.getContext()).inflate(itemViewId, parent, false);
return new ViewHolder(itemView);
}
以上方案纯属针对某种列表来实现的,如果换了一个列表视图咱们就需要重现创建adapter并分别在onCreateViewHolder
,onBindViewHolder
方法中实现不同逻辑并重新创建xxxViewHolder继承ViewHolder来保存不同的Item视图
解决方案
基于以上问题,给出如下方案
LGViewHolder.java
用来设计通用的ViewHolder
public class LGViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;
private View mConvertView;//缓存itemView内部的子View
public LGViewHolder(View itemView) {
super(itemView);
mConvertView = itemView;
mViews = new SparseArray<>();
}
/**
* 加载layoutId视图并用LGViewHolder保存
* @param parent
* @param layoutId
* @return
*/
protected static LGViewHolder getViewHolder(ViewGroup parent, int layoutId) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
return new LGViewHolder(itemView);
}
/**
* 根据ItemView的id获取子视图View
* @param viewId
* @return
*/
public View getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return view;
}
}
LGRecycleViewAdapter.java
public abstract class LGRecycleViewAdapter<T> extends RecyclerView.Adapter<LGViewHolder> {
private final String TAG = "LGRecycleViewAdapter";
//存储监听回调
private SparseArray<ItemClickListener> onClickListeners;
private List<T> dataList;
public interface ItemClickListener {
void onItemClicked(View view,int position);
}
public LGRecycleViewAdapter(List<T> dataList) {
this.dataList = dataList;
onClickListeners = new SparseArray<>();
}
/**
* 存储viewId对应的回调监听实例listener
* @param viewId
* @param listener
*/
public void setOnItemClickListener(int viewId,ItemClickListener listener) {
ItemClickListener listener_ = onClickListeners.get(viewId);
if(listener_ == null){
onClickListeners.put(viewId,listener);
}
}
/**
* 获取列表控件的视图id(由子类负责完成)
* @param viewType
* @return
*/
public abstract int getLayoutId(int viewType);
//更新itemView视图(由子类负责完成)
public abstract void convert(LGViewHolder holder, T t, int position);
public T getItem(final int position){
if(dataList == null)
return null;
return dataList.get(position);
}
@Override
public LGViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layoutId = getLayoutId(viewType);
LGViewHolder viewHolder = LGViewHolder.getViewHolder(parent, layoutId);
return viewHolder;
}
@Override
public void onBindViewHolder(LGViewHolder holder, final int position) {
T itemModel = dataList.get(position);
convert(holder, itemModel, position);//更新itemView视图
//设置点击监听
for (int i = 0; i < onClickListeners.size(); ++i){
int id = onClickListeners.keyAt(i);
View view = holder.getView(id);
if(view == null)
continue;
final ItemClickListener listener = onClickListeners.get(id);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(listener != null){
listener.onItemClicked(v,position);
}
}
});
}
}
@Override
public int getItemCount() {
if (dataList == null)
return 0;
return dataList.size();
}
public void destroyAdapter(){
if(onClickListeners != null)
onClickListeners.clear();
onClickListeners = null;
if(dataList != null)
dataList.clear();
dataList = null;
}
}
使用方法如下:
单视图方式:
定义adapter
private static class MainAdapter extends LGRecycleViewAdapter<String> {
...
@Override
public int getLayoutId(int viewType) {
return R.layout.item_view_main1;
}
@Override
public void convert(LGViewHolder holder, String s, final int position) {
TextView textView = (TextView) holder.getView(R.id.id_text);
textView.setText(s);
}
}
在需要时使用:
mainAdapter = new MainAdapter(datas);
mainAdapter.setOnItemClickListener(R.id.root, new LGRecycleViewAdapter.ItemClickListener() {
@Override
public void onItemClicked(View view, int position) {
Log.d(TAG,"root clicked..." + position);
}
});
mainAdapter.setOnItemClickListener(R.id.icon, new LGRecycleViewAdapter.ItemClickListener() {
@Override
public void onItemClicked(View view, int position) {
Log.d(TAG,"icon clicked..." + position);
}
});
recyclerView.setAdapter(mainAdapter);
可以看出我们轻松的实现了同一Item视图不同子视图的点击监听,并且在MainAdapter子类中只需通过getLayoutId告诉父类Item视图对应的视图id,并在convert方法中只更新视图即可
如果需要支持多视图模式则只需在子类中重现getViewType即可,代码如下:
MainAdapter.java
@Override
public int getLayoutId(int viewType) {
if(viewType == 1)
return R.layout.item_view_main1;
return R.layout.item_view_main2;
}
//支持不同viewType视图
@Override
public int getItemViewType(int position) {
String model = getItem(position);//实际开发中可以通过model的属性来决定返回viewType类型
if(position % 2 == 0)
return 1;
return 2;
}
写在最后
为了简单起见,本篇文章设计的adapter适合没有header和footer视图的RecycleView,至于这方面的功能打算在github上更新
完整的代码及案例可以到我的github下载
demo开源github地址如下:
LGRecycleViewAdapter
欢迎大家访问并star,如果有任何问题可以在评论中加以提问,谢谢~~