您的位置:首页 > 其它

[置顶] 超级简单RecycleViewAdapter的封装-支持多视图,多点击回调

2016-08-09 18:22 429 查看
其实RecycleView已经出来很长时间了,对RecycleView的用法网上也有很多教程了。本篇文章不讲解RecycleView的用法,不讲解LayoutManager的用法也不讲解ItemDecoration的用法,我们只关注Adapter的用法以及如何封装成一个通用的Adapter

Adapter的正常使用方法

其实很简单,只需要继承RecyclerView.Adapter传入范型类型为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至少需要完成以下三件事情

1.
onCreateViewHolder
通过视图Id加载不同Item视图并生成ViewHolder用来保存每个列表Item视图

2.
onBindViewHolder
更新列表Item视图(填充model数据)

3. 新建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,如果有任何问题可以在评论中加以提问,谢谢~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: