一、前文
说起来,RecyclerView使用也不是一时半会了,之前在使用RecyclerView的时候都是直接使用的 https://www.jianshu.com/p/b343fcff51b0 这个框架的 , 时间久了原生的RecyclerView反而感觉变的有些生疏, 刚好最近有些时间便想好好理理RecyclerView的原生基础使用 .
如需转载请务必标明出处,谢谢 !!!
**** 能力有限, 如有错误还望指点...****
二、简述
因为, 这只是一篇学习中的随笔而并非什么科普文, 这里就不详细介绍RecyclerView的过人之处以及相比ListView或者GridView的优缺点了, 这里就只记录一下这次简单的RecyclerView的使用流程.
1、配置
因为RecyclerView并非系统自带基础组件,这里是需要去添加依赖来使用的.
在app的gradle文件中添加一下依赖
implementation 'com.android.support:recyclerview-v7:28.0.0-alpha3'
* 这里解释一下 implementation 与 compile 的区别 :
在新版本 Android Gradle plugin 3.0 中,已经将compile标记为废弃,更改为了implementation 和 api 两个,其中 api的功能和compile其等同的,而implementation添加的依赖只对当前的Module有效
2、使用
首先布局中使用RecyclerView
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@color/colorPrimary" tools:context="com.fordream.learnmvp.recyclerview.activity.RVTActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tv_rvt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_margin="10dp" android:padding="10dp" android:text="添加数据" android:textSize="16sp" /> </LinearLayout> <android.support.v7.widget.RecyclerView android:id="@+id/rv_rvt" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none" /> </LinearLayout>
这里我们在布局当中放置了TextView方便点击之后给RecyclerView添加数据使用 。
在Activity中使用RecyclerView
(这里在做RecyclerView练习的时候,同时也顺便练习了一下MVP的使用,所以下面的代码就只复制关键代码,不全部复制了)
adapter = new RVTAdapter(RVTActivity.this, beans); recyclerView = findViewById(R.id.rv_rvt); recyclerView.setLayoutManager(new LinearLayoutManager(RVTActivity.this)); recyclerView.setAdapter(adapter); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.addItemDecoration(new DividerItemDecoration(RVTActivity.this, DividerItemDecoration.HORIZONTAL));
可以看到在使用RecyclerView的时候在findViewById()之后我们需要进行一下配置:
1、setLayoutManager() // 设置布局管理,也就是布局的样式 我们这里选择的是最常见的 纵向列表的样式 ( 我们还可以设置成网格样式或者瀑布流样式 )
2、setAdapter() //和ListView一样设置适配器 ( 具体的适配器见下文 )
3、setItemAnimator() // 设置列表中插入或删除数据时item的动画效果( 无这方面的需求可不配置该项 )
4、setItemDecoration() //设置分割线
设置完这些RecyclerView便算是配置完了,接下来我们看看RecyclerView的Adapter的样式
RecyclerView的Adapter
首先咱们先看一下Item的布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimaryDark" android:gravity="center_vertical" android:orientation="horizontal" > <ImageView android:id="@+id/iv_rvt_item" android:layout_width="60dp" android:layout_height="60dp"/> <TextView android:id="@+id/tv_rvt_item" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="25dp" android:textSize="14sp" /> </LinearLayout>
我们在Item中放了一个图片和一段文字( 没有什么意义,仅仅为了展示 )
我们在看Adapter的代码
public class RVTAdapter extends RecyclerView.Adapter<RVTAdapter.MyViewHolder> { private Context context; private List<RVTBean> beans; private RVTClickListener listener; public RVTAdapter(Context context, List<RVTBean> beans) { this.context = context; this.beans = beans; } public void setOnClickListener(RVTClickListener onClickListener) { this.listener = onClickListener; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { MyViewHolder holder = new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_rvt, viewGroup, false)); return holder; } public void addDates(List<RVTBean> rvtBeans) { beans.addAll(rvtBeans); notifyDataSetChanged(); } public void addDate(RVTBean rvtBean, int position) { beans.add(position, rvtBean); notifyItemInserted(position); //notifyItemRangeChanged(position, beans.size() - position); } public void deleteDate(int position) { beans.remove(position); notifyItemRemoved(position); //notifyItemRangeChanged(position, beans.size() - position); } @Override public void onBindViewHolder(@NonNull final MyViewHolder myViewHolder, int i) { myViewHolder.tv_name.setText(beans.get(i).getName()); myViewHolder.iv_img.setImageResource(beans.get(i).getImg()); if (listener != null) { myViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { listener.itemOnClickListener(myViewHolder.itemView, myViewHolder.getLayoutPosition()); } }); myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { listener.itemOnLongClickListener(myViewHolder.itemView, myViewHolder.getAdapterPosition()); return false; } }); } } @Override public int getItemCount() { return beans.size(); } class MyViewHolder extends RecyclerView.ViewHolder { private ImageView iv_img; private TextView tv_name; public MyViewHolder(@NonNull View itemView) { super(itemView); iv_img = itemView.findViewById(R.id.iv_rvt_item); tv_name = itemView.findViewById(R.id.tv_rvt_item); } } }
看似代码不少,其实按顺序来并不难,我们一点一点的看
首先我们创建一个Adapter并让其继承 RecyclerView.Adapter , 此时先不慌解决编译器提示的错误 , 我们先在Adapter类中创建一个内部类 MyViewHolder 并继承自 RecyclerView.ViewHolder
然后我们在 RecyclerView.Adapter 之后增添我们刚刚创建的MyViewHolder作为泛型, 即: RecyclerView.Adapter<RVTAdapter.MyViewHolder>
到了这里我们便可以根绝编译器的提示来实现RecyclerView.Adapter内的抽象方法, 分别是 onCreateViewHolder() 、onBindViewHolder()、 getItemCount() 这三个方法
其中在onCreateViewHolder()中顾名思义就是创建ViewHolder ; 在onBindViewHolder()中顾名思义就是绑定数据即为ViewHolder中的布局各组件赋值、设置监听事件等 ; getItemCount() 就是获取数据集合的长度。
在上面代码中,我们看到在我们这里在Adapter的构造方法中我们获取了当前的上下文以及数据集,同时我们添加了setOnClickListener()、addDate()、addDates()、deleteDate()这几个方法,也就是我们根据业务来处理业务的方法。
我们主要是为了实现增加更多数据、插入单条数据、删除数据 , 同时我们对外做了接口的回调来处理Item的单击以及长按事件。
当我们在Activity中点击"加载更多"时我们会调用adapter的addDates()方法, 来更改数据集并且做了刷新显示; 当我们单击某一条Item的时候我们会调用addDate()方法在点击的Item处插入一条新的数据,并作数据刷新处理 ; 当我们长按一条Item的时候我们会弹出提示框是否删除该Item, 确定之后执行Adapter的deleteDate()方法来删除该Item并刷新。
我们来看一下Activity中相关代码:
adapter.setOnClickListener(new RVTClickListener() {
@Override
public void itemOnClickListener(View view, int position) {
Toast.makeText(RVTActivity.this, "第" + position + "条", Toast.LENGTH_SHORT).show();
rvtPersenterImp.addDate(position);//插入一条数据
}
@Override
public void itemOnLongClickListener(View view, final int position) {
final AlertDialog.Builder builder = new AlertDialog.Builder(RVTActivity.this);
builder.setTitle("是否删除该条数据?")
.setMessage("第" + position + "条")
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
rvtPersenterImp.deleteDate(position);//删除一条数据
}
})
.show();
}
});
tv_addMore = findViewById(R.id.tv_rvt);
tv_addMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
rvtPersenterImp.loadMore();//加载更多
}
});
**大家请自动屏蔽MVP有关的rvtPersenterImp**
这里有一个自己踩的坑顺便提一下( 很low的错误,主要是不细心 )
在删除数据时因为使用了Dialog进行提醒,所以在执行rvtPersenterImp.deleteDate()时需要一个int的参数,当时想也没想直接传入了 onClick()中的参数 int i , 结果每次执行删除都会报数组越界的异常, 当时在网上一顿的搜, 大部分都说是RecyclerView本身的一个BUG在删除数据时会导致数据错乱,是因为RecyclerView删除之后只是做了删除的效果,数据集并没有真正的改变, 同时网友也给出了具体的避免方法( 也就是Adapter代码中注释掉的部分 ) , 后来根据这些改了之后并没有改变,而且别人是在 notifyItemRemoved() 报的异常,而我是在benas.remove()方法执行时报错的,后来才发现我传入的参数错误, 不应该传入onClick中的参数 int i 而是回调接口中 itemOnLongClickListener()方法中的参数 int position
到这里RecyclerView的最基本的使用方法也就完了,当然更多的用法也都是在这些基础上进行扩展的,就留待日后发掘吧。
3、总结
在这里给大家介绍两个比较实用的库:
从上面也看出来了,RecyclerView灵活性是很高的,同时带来了一个问题就是很多东西需要我们自己去实现,比如Item的动画效果以及Item之间的分割线, 接下来的两个库自己尝试了一下配置和使用都是十分的方便,分别用在我们上面讲到的recyclerView.setItemAnimator()和recyclerView.addItemDecoration()处即可。
RecyclerView动画库 : https://github.com/wasabeef/recyclerview-animators .
RecyclerView分割线库: https://github.com/yqritc/RecyclerView-FlexibleDivider .