RecyclerView 它是从Android5.0出现的全新列表组件,更加强大和灵活。用于显示列表形式 (list) 或者网格形式 (grid) 的数据,替代ListView和GridView成为Android主流的列表组件。可以说Android客户端只要有表格的地方就有RecyclerView。
RecyclerView 内置 ViewHolder,省去了每次 findViewById() 创建视图;当列表滑动的时候,只有少量相邻的视图会被创建。当视图滑出屏幕时,RecyclerView 会复用它并且填充新的数据。由于它是通过回收已有的结构而不是持续创建新的列表项,所以它可以有效提高应用的时间效率和空间效率。
RecyclerView 拥有三种 LayoutManager,LinearLayoutManager
它支持纵向滑动的列表和横向滑动的列表;GridLayoutManager
实现网格布局的列表;StaggeredGridLayoutManager
实现瀑布流布局。并且支持自定义 LayoutManager;
RecyclerView 还可以定制一些动画增加趣味性。
稍微繁琐的一点是RecyclerView没有直接提供点击和长按事件处理方法,没有提供ItemClickListener和ItemLongClickListener,因此我们可以给某个控件加点击事件然后自己通过一个接口回调放到Activity'或者Fragment中来处理事件,个人认为这样方便操作数据源。
上面提到它是在v7包,所以一定不要忘了导包哈。
RecyclerView功能强大,本篇文章先记录横竖方向的线性列表和网格布局,瀑布流使用与这两者相似请自行实验;列表滑动、点击事件更新列表以及线程切换更新等。
- build.gradle引用RecyclerView
使用support.V7 包的需要导包,androidx包的不用单独引用
2. xml文件声明
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerAct_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- findViewById并使用
recycler = findViewById(R.id.recyclerAct_recycler);
- 设置LayoutManager
4.1竖向布局
if (linearLayoutManager == null){
linearLayoutManager = new LinearLayoutManager(RecyclerViewActivity.this);
}
recycler.setLayoutManager(linearLayoutManager);
此处可以设置横向,默认是竖向
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
4.2网格布局
if (gridLayoutManager == null){
gridLayoutManager = new GridLayoutManager(RecyclerViewActivity.this, 3);
}
recycler.setLayoutManager(gridLayoutManager);
瀑布流布局StaggeredGridLayoutManager可自行试验
5.定义Adapter继承RecyclerView.Adapter
通过源码可以看出Adapter是RecyclerView的一个内部类,他还是个抽象类,内部onCreateViewHolder、onBindViewHolder、getItemCount三个从抽象方法,我们实现的子类必须重写,分别是创建布局、渲染数据、数据源数量。
通过源码可以看出Adapter是RecyclerView的一个内部类,他还是个抽象类,但无抽象方法。
还定义了changeData这个方法,如果修改了数据源可以调用它来进行刷新:
public void changeData(List<String> data){
mData.clear();
mData.addAll(data);
notifyDataSetChanged();
}
6. setAdapter并传递数据进行展示
adapter = new GridLayoutAdapter(this);
recycler.setAdapter(adapter);
7.点击事件处理
没有提供ItemClickListener和ItemLongClickListener,因此我们可以给某个控件加点击事件然后自己通过一个接口回调放到Activity'或者Fragment中来处理事件,我个人认为这样方便操作源数据。
定义接口:
public void setItemClickListener(OnItemClickListener itemClickListener){
mItemClickListener = itemClickListener;
}
public interface OnItemClickListener {
void onItemClick(int positon);
}
点击事件:
@Override
public void onBindViewHolder(@NonNull GridLayoutAdapter.ViewHolder holder, int position) {
holder1.name.setText(mData.get(position));
holder1.name.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mItemClickListener.onItemClick(position);
selectPosition = position;
}
});
}
7.1 列表竖向和表格切换,仿电商App
changeTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (adapter == null){
adapter = new GridLayoutAdapter(RecyclerViewActivity.this);
}
if ("竖向".equals(changeTV.getText().toString().trim())){
if (gridLayoutManager == null){
gridLayoutManager = new GridLayoutManager(RecyclerViewActivity.this, 3);
}
recycler.setLayoutManager(gridLayoutManager);
changeTV.setText("网格");
}else {
if (linearLayoutManager == null){
linearLayoutManager = new LinearLayoutManager(RecyclerViewActivity.this);
}
// linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recycler.setLayoutManager(linearLayoutManager);
changeTV.setText("竖向");
}
recycler.setAdapter(adapter);
adapter.changeData(mDataList);
}
});
7.2 将列表滑到指定索引的item
recycler.smoothScrollToPosition(42);
滑动到制定索引的位置,是指的最下方漏出来哪一条。有滑动过渡的效果。所以超过item数据总和的总都会适配直接滑动最极限值(最后一条),不会报错
recycler.scrollBy(0,360);
滑动到制定宽高的位置,是指的最下方一条的位置。没有滑动过渡的效果。scrollBy相对初始位置每次移动这些距离,超过recycler的总高度或宽度都会适配直接滑动最极限值(总宽和总高),不会报错
recycler.scrollTo(0,-2260); 好坑啊
试了几个值,但都没有滑动,好坑啊,源码是个空壳打印了一行Warn级别的日志:2024-06-27 16:41:12.102 29806-29806/包名W/RecyclerView: RecyclerView does not support scrolling to an absolute position. Use scrollToPosition instead,重写了父类View就是为了毁灭它吧,
addOnScrollListener可以监听recycler的滑动状态和滑动位置:
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
Log.e(TAG, "onScrollStateChanged: newState= "+newState );
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Log.e(TAG, "onScrolled: dx= "+dx+" dy= "+dy );
}
});
7.3 点击修改item背景
//设置item背景颜色
if (selectPosition == position){
holder1.name.setBackgroundColor(mActivity.getResources().getColor(R.color.red));
}else {
holder1.name.setBackgroundColor(mActivity.getResources().getColor(R.color.white));
}
7.4 点击删除指定item
7.4.1直接这样操作没有没问题,但是很生硬不死话
mDataList.remove(positon);
adapter.changeData(mDataList);
7.4.2
使用API方法加一下删除动画
mDataList.remove(positon);
adapter.notifyItemRemoved(positon);
adapter.notifyChanged(positon,mDataList);
注意:notifyItemRemoved方法比较坑,调用notifyItemRemoved以后一定要再调用notifyItemRangeRemoved这个方法,并且要有设置给recycler的数据的直接变化,直白点就是getItemCount方法中标示长度的哪个数据源,修改最最源头源头的数据源二不改变这个是无效的
getItemCount方法是上面第5条提过的返回数据源数量。notifyChanged和面第5条提过changeData方法一样,不过吧notifyDataSetChanged();换成了notifyItemRangeRemoved(position,mData.size() - position);
public void notifyChanged(int position,List<String> data){
mData.clear();
mData.addAll(data);
//调用notifyItemRemoved以后一定要再调用这个方法,并且要有设置给recycler的数据的直接变化,直白点就是getItemCount中标示长度的哪个数据源,修改最最源头源头的数据源二不改变这个是无效的
notifyItemRangeRemoved(position,mData.size() - position);
}
7.5 子线程切换到主线程更新UI
因为RecyclerView继承ViewGroup,而ViewGroup继承View,所以这里直接使用了爷类View的post方法。
recycler.post(new Runnable() {
@Override
public void run() {
可执行页面刷新操作
}
});
才疏学浅,如有错误,欢迎指正,多谢。