前言:人在无端微笑时,不是百无聊赖,就是痛苦难当。有些笑容的背后是咬紧牙关的灵魂。 ——看见 -柴静
自Android5.0之后Google推出了新的列表控件RecyclerView,代替了经典的ListView,是一个强大的滑动组件,拥有item的复用回收功能,更加高级和灵活。此组件是一个用于显示庞大数据集的容器,可通过保持有限数量的视图进行非常有效的滚动操作。如果您有数据集合,其中的元素将因用户操作或者网络事件而发生改变,建议使用RecyclerView。
官方对它的介绍:
A flexible view for providing a limited window into a large data set.
大意:为大量数据集提供一个有限的展示窗口的灵活视图。列表是app非常常见的,怎么展示大量的数据是个技术活,特别是在手机捉襟见肘的内存中显得尤为重要,怎么在微小的内存中显示大量的数据?
RecyclerView通过提供下列功能来简化庞大数据集的显示与处理:
如果要使用RecyclerView组件,必须指定一个Adapter和一个LayoutManager。通过继承RecyclerView.Adapter的方式可以自定义一个Adapter。实现Adapter的详情将取决于数据data集合的具体信息以及视图view的类型。
LayoutManager决定RecyclerView内各项item的位置并决定何时重新使用用户已不可见的item。
如果要重新使用一个item,LayoutManager可能会要求Adapter以数据集合中的另一组数据替换item的内容。以此方式重复使用视图将可避免创建不必要的视图或执行非常耗的findViewById,从而改善性能。
Adapter适配器的功能是为数据源中的每一个Item创建一个视图,并为他们设置相应的数据,当之前的Item不可见时,以新的Item进行替换。
RecyclerView默认情况下,当增加或者删除Item时,带有动画效果。如果要定制某些其他效果的动画,需要扩展RecyclerView.ItemAnimator类,并使用RecyclerView.setItemAnimator设置所定制的动画。
官方介绍RecyclerView是ListView的升级版,RecyclerView相对ListView的优点如下:
contentView==null,convertView.setTag(holder),holder=(ViewHolder)convertView.getTag()
的繁琐操作;ItemDecoration
针对自己的业务逻辑进行处理;ItemAnimator
可以控制item的增删动画。但是关于RecyclerView的item的相关点击事件需要自己高度自定义,不像ListView那样item点击事件能快速响应。
以下是RecyclerView内部的几个重要类,简单了解下,后面的文章都有详细涉及相关的内容。
类名 | 作用 |
---|---|
RecyclerView.Adapter | 为每一项item创建视图 |
RecyclerView.ViewHolder | 承载item视图的子布局 |
RecyclerView.LayoutManager | 负责item视图的布局显示管理 |
RecyclerView.Recycler | 负责处理item的回收复用,即缓存 |
RecyclerView.ItemDecoration | 给每一项item视图添加子View,如分割线 |
RecyclerView.ItemAnimator | 负责处理数据添加或者删除时的动画效果 |
RecyclerView是support-v7的组件,我们要在build.gradle
文件中加入RecyclerView的依赖:
添加RecyclerView的依赖:
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha01'
我们先来实现简单的列表效果,如下图:
在Activity的布局文件中加入RecyclerView控件
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
androidx.constraintlayout.widget.ConstraintLayout>
然后我们在Activity中通过findViewById()
查找到该控件
RecyclerView recyclerView = findViewById(R.id.recyclerView);
RecyclerView能支持各种各样的布局效果,其关键核心在 RecyclerView.LayoutManager类中,RecyclerView的过程要比ListView的过程多一个setLayoutManager()
的步骤,它是控制item最终的展示效果;LayoutManager负责RecyclerView的布局,包含了ItemView的获取和回收。下面会讲解
//创建布局管理器-线性布局
LinearLayoutManager manager = new LinearLayoutManager(this);
//设置管理器的水平方向,RecyclerView.VERTICAL垂直方向,RecyclerView.HORIZONTAL水平方向
manager.setOrientation(RecyclerView.VERTICAL);//RecyclerView.HORIZONTAL
//设置布局布局管理器到recyclerView
recyclerView.setLayoutManager(manager);
Adapter适配器的功能是为数据集合中的每一个Item创建一个ViewHolder,并为他们设置相应的数据,当之前的Item不可见时,以新的Item进行替换。Adapter其实就是为RecyclerView和数据绑定起来,将数据设置到具体的item中,这里的Adapter必需要继承自RecyclerView.Adapter
这里adapter我们将数据和上下文通过构造函数带进来,实现上面的三个方法,adapter如下:
public class LinearVerticalAdapter extends RecyclerView.Adapter {
public LinearVerticalAdapter(Context context, List<String> stringList) {
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 0;
}
}
这里RecyclerView的adapter和ListView的adapter有点不同的是RecyclerView把ListView的getView()
方法拆分为绑定View的 onCreateViewHolder()
和绑定数据的onBindViewHolder()
。
接下来创建一个ViewHolder,ViewHolder就是为了保存每一个item的视图控件元素,它需要使用到item的视图,我们这里先创建item的布局文件 item_linear.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#e7e7e7"
android:paddingLeft="12dp"
android:textColor="#3175ff"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="item"
android:textSize="16sp" />
LinearLayout>
里面只是简单的TextView,我们先创建ViewHolder,它需要继承自RecyclerView.ViewHolder,ViewHolder主要作用是将item中的控件以变量的形式保存起来,方便后面的数据绑定;
ViewHolder.java
public class ViewHolder extends RecyclerView.ViewHolder {
//控件变量
TextView mTv_name;
ViewHolder(@NonNull View itemView) {
super(itemView);
mTv_name = itemView.findViewById(R.id.tv_name);
mTv_name.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, mTv_name.getText().toString(), Toast.LENGTH_SHORT).show();
}
});
}
}
创建ViewHolder时,通过构造方法传入itemView,复写super(itemView)
方法,将控件TextView从itemView里面取出,保存在mTv_name常量中,这样就能通过holder实例使用到控件。
创建ViewHolder后就要使用它填充好Adapter的函数,首先是onCreateViewHolder()
:
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_recyclerview, parent, false));
}
每一个item的创建都会调用一次onCreateViewHolder()
,每次调用一次onCreateViewHolder()
都会创建一个ViewHolder,每一个item inflater
一个view,存入ViewHolder中,返回ViewHolder实例。
然后在onBindViewHolder()
中,将ViewHolder与数据绑定起来;
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.mTv_name.setText(mData.get(position));
}
每一个item都会走onBindViewHolder()
方法,正是这个方法将数据与空件绑定起来,通过holder获取相关的Item控件,然后就可以对这些控件进行你需要的操作,比如点击事件,设置相关数据等。
最后在getItemCount()
返回数据的个数;
@Override
public int getItemCount() {
return mData.size();
}
到这里,完整的Adapter就实现了,如下代码:
LinearVerticalAdapter.java
public class LinearVerticalAdapter extends RecyclerView.Adapter {
private Context mContext;
private List<String> mData;
//1.构造方法带入相关参数
public LinearVerticalAdapter(Context context, List<String> stringList) {
this.mContext = context;
this.mData = stringList;
}
//2.用于得到ViewHolder,通过ViewHolder承载视图中的元素,会为每一个item inflate出一个view,封装到ViewHolder中
//viewType:区分不同item的类型
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_recyclerview, parent, false));
}
//3.将指定位置的数据和视图绑定起来,适配渲染数据到View中,因为这里ViewHolder,里面包含了item的View
//holder: 上面创建的ViewHolder,用于查找holder里面的控件,设置数据
//position:item的位置
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.mTv_name.setText(mData.get(position));
}
//4.指定RecyclerView有多少个Item
@Override
public int getItemCount() {
return mData.size();
}
}
最后创建模拟数据,填充到Adapter中,将Adapter设置给RecyclerView,这就基本实现了简单的RecyclerView列表功能;
List<String> stringList = new ArrayList<>();
for (int i = 0; i < 50; i++) {
stringList.add("第 " + i + " 个item");
}
构造adapter同过RecyclerView的setAdapter()
方法设置到RecyclerView中,这样adapter才能与RecyclerView联系起来:
LinearVerticalAdapter adapter = new LinearVerticalAdapter(this, stringList);
recyclerView.setAdapter(adapter);//设置适配器给列表
Activity的完整代码如下:(源码地址在最后给出)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1.查找控件
RecyclerView recyclerView = findViewById(R.id.recyclerView);
//2.创建布局管理器-线性布局
LinearLayoutManager manager = new LinearLayoutManager(this);
//设置管理器的水平方向,RecyclerView.VERTICAL垂直方向,RecyclerView.HORIZONTAL水平方向
manager.setOrientation(RecyclerView.VERTICAL);//RecyclerView.HORIZONTAL
//设置布局布局管理器到recyclerView
recyclerView.setLayoutManager(manager);
//3.设置数据
List<String> stringList = new ArrayList<>();
for (int i = 0; i < 50; i++) {
stringList.add("第 " + i + " 个item");
}
//4.数据适配器
LinearVerticalAdapter adapter = new LinearVerticalAdapter(this, stringList);
//设置适配器到recyclerView
recyclerView.setAdapter(adapter);
}
前面提到,RecyclerView能支持各种各样的效果,主要是因为LayoutManager布局管理器用于控制RecyclerView的最终展示效果。
它决定RecyclerView内各项item的位置并决定item何时回收复用。如果要重新使用(或者重复使用)一个item,LayoutManager可能会要求Adapter以数据集合中的另一组数据替换item的内容。以此方式重复使用视图将可避免创建不必要的视图或执行非常耗的findViewById,从而改善性能。RecyclerView提供了三终常用的布局管理器:
如果你想要达到自己自定义的效果,则应该去继承LayoutManager,重写相关的方法,而不是去改造RecyclerView,相关效果和相关API我们在下一篇文章展示。(源码地址在文章最后给出)
我们可以通过LayoutManager设置RecyclerView的列表方向是垂直方向还是水平方向,默认是垂直方向:
其实只需要在LayoutManager中通过setOrientation()
设置方向就可以了,也可以在构造方法中直接声明方向,这里以线性布局为例,提供了可用的两种构造方法,
逆向展示的意思是在结束处开始布局,即在第一个item在列表最下面,最后一个item在最上面,从下面开始类增,具体可以自行试一试,基于上面的列子,我们将布局管理器方向设置为水平方向,为了方便效果,将item的宽高调整了一下,
manager.setOrientation(RecyclerView.HORIZONTAL)
GridLayoutManager的主要作用是将item用于网格摆放,实现网格的布局效果,RecyclerView的布局样式主要是由布局管理器来控制的,我们只需要改下LayoutManager这块的代码就可以了,先来看看GridLayoutManager的构造方法:
//1.查找控件
RecyclerView recyclerView = findViewById(R.id.recyclerView);
//2.创建布局管理器,列数为3,垂直方向
GridLayoutManager manager = new GridLayoutManager(this, 3, RecyclerView.VERTICAL, false);
//设置布局布局管理器到recyclerView
recyclerView.setLayoutManager(manager);
//3.设置数据
List<String> stringList = new ArrayList<>();
for (int i = 0; i < 50; i++) {
stringList.add("第 " + i + " 个item");
}
//4.数据适配器
GridAdapter adapter = new GridAdapter(this, stringList);
//设置适配器到recyclerView
recyclerView.setAdapter(adapter);
效果如下左图,同时可以将线性方向设置为水平方向,如下右图:
//行数为3,水平方向
GridLayoutManager manager = new GridLayoutManager(this, 3, RecyclerView.HORIZONTAL, false);
StaggeredGridLayoutManager是瀑布流管理器,主要用来瀑布流效果,它只有一个构造方法
那么我们把LayoutManager改为StaggeredGridLayoutManager,设置列数为3,垂直方向,
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(3, RecyclerView.VERTICAL)
注意,如果item的高度或者宽度不变的话,效果和网格布局的效果一样的,如下图,没有改变高度的瀑布流效果:
那么在垂直方向分别时,我们手动改变item的高度,在水平方向分布时,我们就改变item的宽度,这样就可以看到瀑布流的效果了,这只是模拟瀑布流的效果,具体还需要根据实际业务操作;在adapter定义了一个获取高度或者宽度的方法,根据position返回300,400,200三个数值。
private int getRandom(int position) {
if (position % 3 == 0) {//第一排
return 300;
} else if (position % 3 == 1) {//第二排
return 400;
} else {
return 200;
}
}
然后在onBindViewHolder()
方法中根据传入的线性mLinear来设置宽度或者宽度
ViewGroup.LayoutParams layoutParams = viewHolder.mTv_name.getLayoutParams();
if (mLinear == RecyclerView.VERTICAL) {//垂直方向,随机高度
layoutParams.height = getRandom(position);
} else if (mLinear == RecyclerView.HORIZONTAL) {//水平方向,随机宽度
layoutParams.width = getRandom(position);
}
viewHolder.mTv_name.setLayoutParams(layoutParams);//设置控件的参数
改写后的效果如下:左图为垂直方向,有图为水平方向
下面贴出瀑布流的代码:StaggeredGridActivity.java
public class StaggeredGridActivity extends AppCompatActivity implements View.OnClickListener {
private RecyclerView mRecyclerView;
private List<String> mStringList;
private StaggeredGridLayoutManager mManager;
private StaggeredGridAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid_recyclerview);
//1.查找控件
Button btn_vertical = findViewById(R.id.btn_vertical);
btn_vertical.setOnClickListener(this);
findViewById(R.id.btn_horizontal).setOnClickListener(this);
mRecyclerView = findViewById(R.id.recyclerView);
//3.设置数据
mStringList = new ArrayList<>();
for (int i = 0; i < 50; i++) {
mStringList.add("第 " + i + " 个item");
}
//4.数据适配器
mAdapter = new StaggeredGridAdapter(this, mStringList);
btn_vertical.performClick();
}
@Override
public void onClick(View v) {
//2.创建布局管理器
switch (v.getId()) {
case R.id.btn_vertical://垂直方向,列数为3
mManager = new StaggeredGridLayoutManager(3, RecyclerView.VERTICAL);
mAdapter.setLinear(RecyclerView.VERTICAL);
break;
case R.id.btn_horizontal://水平方向,行数为3
mManager = new StaggeredGridLayoutManager(3, RecyclerView.HORIZONTAL);
mAdapter.setLinear(RecyclerView.HORIZONTAL);
break;
}
//设置布局布局管理器到recyclerView
mRecyclerView.setLayoutManager(mManager);
//设置适配器到recyclerView
mRecyclerView.setAdapter(mAdapter);
}
}
瀑布流适配器代码:StaggeredGridAdapter.java
public class StaggeredGridAdapter extends RecyclerView.Adapter {
private Context mContext;
private List<String> mData;
private int mLinear;//线性方向
public StaggeredGridAdapter(Context context, List<String> stringList) {
this.mContext = context;
this.mData = stringList;
}
//用于得到ViewHolder,通过ViewHolder承载视图中的元素,会为每一个item inflate出一个view,封装到ViewHolder中
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_grid, parent, false));
}
//将指定位置的数据和视图绑定起来,适配渲染数据到View中,因为这里ViewHolder,里面包含了item的View
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.mTv_name.setText(mData.get(position));
ViewGroup.LayoutParams layoutParams = viewHolder.mTv_name.getLayoutParams();
if (mLinear == RecyclerView.VERTICAL) {//垂直方向,随机高度
layoutParams.height = getRandom(position);
} else if (mLinear == RecyclerView.HORIZONTAL) {//水平方向,随机宽度
layoutParams.width = getRandom(position);
}
viewHolder.mTv_name.setLayoutParams(layoutParams);
}
//指定RecyclerView有多少个Item
@Override
public int getItemCount() {
return mData.size();
}
//创建ViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
TextView mTv_name;
ViewHolder(@NonNull View itemView) {
super(itemView);
mTv_name = itemView.findViewById(R.id.tv_name);
mTv_name.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, mTv_name.getText().toString(), Toast.LENGTH_SHORT).show();
}
});
}
}
//设置线性方向
public void setLinear(@RecyclerView.Orientation int linear) {
this.mLinear = linear;
}
//获取高度或者宽度,分别返回300,400,200
private int getRandom(int position) {
if (position % 3 == 0) {//第一排
return 300;
} else if (position % 3 == 1) {//第二排
return 400;
} else {
return 200;
}
}
}
这里列出了LayoutManager常见的API,平常都比较需要使用到,特别是在设置能否滚动,滚动位置,滚动方向,以及下拉刷新和上拉加载更多都需要用到这些API。
//能否横向滚动
canScrollHorizontally();
//能否纵向滚动
canScrollVertically();
//滚动到指定位置
scrollToPosition(int position);
//设置滚动的方向
setOrientation(int orientation);
//获取滚动方向
getOrientation();
//获取指定位置的Item View
findViewByPosition(int position);
//获取第一个完全可见的Item位置
findFirstCompletelyVisibleItemPosition();
//获取第一个可见Item的位置
findFirstVisibleItemPosition();
//获取最后一个完全可见的Item位置
findLastCompletelyVisibleItemPosition();
//获取最后一个可见Item的位置
findLastVisibleItemPosition();
源码地址
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。
我是suming,感谢各位的支持和认可,您的点赞、评论、收藏【一键三连】就是我创作的最大动力,我们下篇文章见!
如果本篇博客有任何错误,请批评指教,不胜感激 !
要想成为一个优秀的安卓开发者,这里有必须要掌握的知识架构,一步一步朝着自己的梦想前进!Keep Moving!
相关文章:
理解RecyclerView(一)
● RecyclerView的基础使用、网格布局、瀑布流布局
理解RecyclerView(二)
● RecyclerView的ItemType(不同条目类型)
理解RecyclerView(三)
● RecyclerView的ItemDecoration分割线、增删item动画效果、拖拽和侧滑删除功能
理解RecyclerView(四)
● RecyclerView的自定义点击事件、万能ViewHolder和Adapter简单封装