资料参考
https://blog.csdn.net/lmj623565791/article/details/51118836
https://www.jianshu.com/p/74b6e88905ba
https://www.jianshu.com/p/7fdfea845937
原始的Recycleview参考我的学习笔记
https://mp.csdn.net/postedit/80239307
由于实际应用中,每次填充一个数据类型,就要重新写一遍Adapter,另外,每次,也只能支持一个layout布局,功能有限,在原有的基础上,添加新的功能
基本思路是对Recyclerview进行封装,变成一个抽象类,把视图加载和数据绑定等功能交给子类去实现,减少代码书写量
一 支持泛型
public abstract class RecyclerViewAdapter<T> extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder<T>>
{
List<T> dataList;
//默认构造器
public RecyclerViewAdapter(List<T> dataList) {
this.dataList = dataList;
}
@Override
public RecyclerViewAdapter.ViewHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
View view= createItemView(parent);
ViewHolder<T> holder=createHolder(view);
return holder;
}
// TODO 提供抽象方法,加载不同的布局,初始化不同的子控件
protected abstract ViewHolder<T> createHolder(View view);
protected abstract View createItemView(ViewGroup parent);
// bind()方法绑定data和holder自身
@Override
public void onBindViewHolder(RecyclerViewAdapter.ViewHolder<T> holder,int position) {
T data=dataList.get(position);
holder.bind(data,this);
}
@Override
public int getItemCount() {
return dataList.size();
}
public static abstract class ViewHolder<T> extends RecyclerView.ViewHolder{
T data;
// 默认构造器
public ViewHolder(View itemView) {
super(itemView);
}
public void bind(T data,RecyclerViewAdapter<T> adapter) {
this.data=data;
onBindChild(data);
}
// TODO 绑定item视图中的子控件
protected abstract void onBindChild(T data);
}
}
这样在使用的时候,只需要重写TODO的三个抽象方法即可
比如新建一个MyApp,他的布局文件id是R.layout.layout_myapp,其中有一个ImageView和一个TextView
public class MyApp {
private String name;
private int resId;
public MyApp(String name, int resId) {
this.name = name;
this.resId = resId;
}
public String getName() {
return name;
}
public int getResId() {
return resId;
}
}
开始编写他的adapter类
public class MyAppAdapter extends RecyclerViewAdapter<MyApp> {
public MyAppAdapter( List<MyApp> datasList) {
super(datasList);
}
@Override
protected ViewHolder<MyApp> CreateHolder(View view) {
return new MyAppHolder(view);
}
@Override
protected View CreateItemView(ViewGroup parent) {
return LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_myapp,parent,false);
}
class MyAppHolder extends ViewHolder<MyApp>{
private ImageView mImageView;
private TextView mTextView;
public MyAppHolder(View itemView) {
super(itemView);
mImageView=(ImageView)itemView.findViewById(R.id.iv_myapp);
mTextView=(TextView)itemView.findViewById(R.id.tv_myapp);
}
@Override
protected void onBindChild(MyApp data) {
this.mTextView.setText(data.getName());
this.mImageView.setImageResource(data.getResId());
}
}
}
这样针对不同的数据类型,不同的item布局,我们只用写很简单的逻辑就可以了,我们工作量减少了不少
现在的RecyclerView还是不能点击的,我们需要设置监听器
监听器的设置可以参考笔记中的方法
点击事件有两种
一种是itemView的点击,我们通过原型继承OnClickListener和我们自定义的接口,在Onclick()中回调我们自定义的监听器接口方法,然后重写我们自定义接口方法实现
另一种是itemView中子控件的点击效果
如果跟点击数据跟adapter无关,可以在onBindChild()中自己设置点击监听
如果跟adapter数据有关系,可以在onBindListener(holder)中设置点击事件
因为更改adapter需要有adpter的引用,所以,通过回调实现
1,定义回调接口
public interface OnItemClickListener{
void ItemClick(View view,int position);
}
2.让原型adapter继承OnclickListener接口和我们定义的回调接口
定义一个OnItemClickListener变量,并在初始化的时候赋值
public abstract class RecyclerViewAdapter<T> implements View.OnClickListener,OnItemClickListener<T>, AdapterCallback<T>
{
...
private OnItemClickListener<T> mListener;
BaseRecyclerViewAdapter(List<T> dataList) {
this.dataList = dataList;
this.mListener=this;
}
...
}
3.接下来,在Onclick事件中转发我们的接口方法
//首先在初始化itemView过程中注册监听器
@Override
public BaseRecyclerViewAdapter.ViewHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
View view= createItemView(parent);
view.setOnClickListener(this);
ViewHolder<T> holder= createHolder(view);
holder.callback=this;
view.setTag(holder);
return holder;
}
//然后在Onclick()方法中转发我们的ItemClick()方法,之后我们只需要重写ItemClick()方法就实现了对Itemview的点击事件
@Override
public void onClick(View v) {
ViewHolder holder=(ViewHolder) v.getTag();
int position=holder.getAdapterPosition();
this.mListener.ItemClick( holder,dataList.get(position));
}
4.itemView中子控件的点击事件
首先是与adapter数据无关的点击
可以在onBindChild()中自己实现,子控件在创建viewHolder的时候初始化
@Override
protected void onBindChild(MyApp data) {
this.mTextView.setText(data.getName());
this.mImageView.setImageResource(data.getResId());
this.mImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
另外就是与adapter有关的点击方法
首先,在外部定义一个一个回调接口
public interface AdapterCallback<T> {
void update(T data, BaseRecyclerViewAdapter.ViewHolder<T> holder);
}
然后让adapter也继承这个接口
public abstract class BaseRecyclerViewAdapter<T> implements View.OnClickListener,OnItemClickListener<T>, AdapterCallback<T>
因为是viewHolder来引用adapter
所以,在ViewHolder中增加一个接口变量,,并在creatHolder()时初始化赋值
@Override
public BaseRecyclerViewAdapter.ViewHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
View view= createItemView(parent);
view.setOnClickListener(this);
ViewHolder<T> holder= createHolder(view);
holder.callback=this;
view.setTag(holder);
return holder;
}
然后在ViewHolder中调用callBack变量的方法
void updateData(T data){
if (callback!=null){
this.callback.update(data,this);
}
}
具体的点击事件我们放在callback.update()中实现,
比如添加,删除itemView等等.具体示例
分两部分,另外,我们需要给原型补充一个get方法,方便引用datalist并改动
List<T> getDataList() {
return dataList;
}
第一部分,
@Override
protected void onBindListener(ViewHolder<MyApp> holder) {
mImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
updateData(null);
}
});
}
第二部分
@Override
public void update(MyApp data, ViewHolder<MyApp> holder) {
List<MyApp> list=getDataList();
int position=holder.getAdapterPosition();
list.remove(position);
notifyDataSetChanged();
}
示例代码
https://github.com/wwqby/BaseRecyclerView.git
填坑
1 holder.getAdapterPosition()返回-1
https://blog.csdn.net/newmandirl/article/details/50854733
调用了RecyclerView的Adapter的notifyDatasetChanged方法后刚好马上执行了View的performClick。
notifyDatasetChanged将所有的ViewHolder设置了flag ,意思
2如何给RecyclerView选项视图中的某个控件单独设置监听
直接在Viewholder中添加并初始化控件,然后在bind()方法中给控件设置单独监听
避免在itemClick()事件中写
首先因为itemClick()转发自ItemView的点击事件,会先调用V.getAdapterPosition().如果给子控件注册点击事件直接报错空指针.其次,子控件的点击事件在onBindechild()中就有