RecyclerView的封装实现

资料参考

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()中就有


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值