一、前言
<font face = 黑体>可以这样说,RecyclerView 的问世,替代了 ListView 和 GridView。RecyclerView 异常的灵活、可自定义并可重复利用的 Item 、高度的解耦,并且通过设置不同的 LayoutManager、ItemDecoration 和 ItemAnimator 可以实现令人瞠目的效果。
<font face = 黑体>ItemDecoration 的相关讲解大家可以看 这篇文章。
<font face = 黑体>源码已经上传至 github,地址在文末中已经给出,可以先下载到本地运行起来,因为具体事例讲解的时候并没有把全部的代码贴出来。
二、与 RecyclerView 配合的类
- <font face = 黑体>LayoutManager:布局管理器,管理 RecyclerView 的展示样式,系统提供了列表、网格和瀑布流三种形式的布局管理器。
- <font face = 黑体>Adapter:适配器,用来处理视图与数据之间的关系。
- <font face = 黑体>ViewHolder:用来容纳 View 视图。
三、案例介绍
<font face = 黑体>我们要实现的最终效果如下所示:
<font face = 黑体>可以看到界面中有四个按钮,分别是添加数据、切换布局,插入一条数据和删除一条数据。当点击添加数据的时候,RecyclerView 就会以线性布局的方式展示出数据,当点击切换布局的时候,RecyclerView 会先切换成网格布局,然后再切换成瀑布流布局,当点击插入和删除数据的时候,RecyclerView 会根据默认的动画来执行,并且动态更新 ItemView 的 position。
四、实现线性布局列表
4.1、引入 RecyclerView
<font face = 黑体>要使用 RecyclerView,首先我们需要在 app 的 build.gradle 中引入 RecyclerView 库。
implementation 'androidx.recyclerview:recyclerview:1.1.0'
4.2、创建适配器
<font face = 黑体>想要把数据展示在列表视图中,就需要通过适配器,所以我们这里创建 MyRecyclerViewAdapter 继承自 RecyclerView 的 Adapter,并实现其中的三个方法,这里我们以内部类的方式来实现 MyViewHolder,同样是继承自 RecyclerView 的 ViewHolder,具体代码如下所示:(源码文末给出)
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {
private Context mContext;
private List<String> dataSource;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public MyRecyclerViewAdapter(Context mContext) {
this.mContext = mContext;
this.dataSource = new ArrayList<>();
}
public void setDataSource(List<String> dataSource) {
this.dataSource = dataSource;
notifyDataSetChanged();
}
/**
* 创建并且返回 ViewHolder
*
* @param parent
* @param viewType
* @return
*/
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false));
}
/**
* 通过 ViewHolder 来绑定数据
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
holder.mIv.setImageResource(getIcon(position));
holder.mTv.setText(dataSource.get(position));
}
/**
* 返回数据数量
*
* @return
*/
@Override
public int getItemCount() {
return dataSource.size();
}
// 获取图片
private int getIcon(int position) {
switch (position % 5) {
case 0:
return R.drawable.a;
case 1:
return R.drawable.b;
case 2:
return R.drawable.c;
case 3:
return R.drawable.d;
case 4:
return R.drawable.e;
}
return 0;
}
class MyViewHolder extends RecyclerView.ViewHolder {
private ImageView mIv;
private TextView mTv;
private View mItemView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
mIv = itemView.findViewById(R.id.iv);
mTv = itemView.findViewById(R.id.tv);
mItemView = itemView;
}
}
}
4.3、在 MainActivity 使用适配器
<font face = 黑体>在 MainActivity 中实例化 Adapter,掉用 RecyclerView .setAdapter() 方法来显示数据,当点击添加数据的时候,就将模拟数据通过 setDataSource() 方法设置过去,在该方法中我们写了 notifyDataSetChanged() 方法,所以,数据设置完后会通知界面展示。
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private MyRecyclerViewAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.recycler_view);
// 线性布局
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
mAdapter = new MyRecyclerViewAdapter(this, mRecyclerView);
mRecyclerView.setAdapter(mAdapter);
}
/**
* 添加数据
* @param v
*/
public void onAddDataClick(View v) {
List<String> data = new ArrayList<>();
for (int i = 0; i < 20; i++) {
String s = "第" + i + "条数据";
data.add(s);
}
mAdapter.setDataSource(data);
}
}
4.4、效果展示
<font face = 黑体>具体效果如下所示:
五、切换布局
5.1、网格布局
<font face = 黑体>RecyclerView 的布局是通过 LayoutManager 来设置的,使用网格布局只需要实例化 GridLayoutManager,并调用RecyclerView.setLayoutManager() 方法就可以了, 以下代码中的 2 的意思是每一行都有两列数据。
public void onChangeLayoutClick(View v) {
// 网格布局
if (mRecyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
mRecyclerView.setLayoutManager(gridLayoutManager);
}
}
5.2、瀑布流布局
<font face = 黑体>所谓瀑布流布局就是指 ItemView 的宽度相同,但是高度不同。同样,我们只需要实例化 StaggeredGridLayoutManager 并设置到 RecyclerView 里面去就可以了。
public void onChangeLayoutClick(View v) {
// 网格布局
if (mRecyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
mRecyclerView.setLayoutManager(gridLayoutManager);
}
// 瀑布流布局
else if (mRecyclerView.getLayoutManager().getClass() == GridLayoutManager.class) {
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
}
// 线性布局
else if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
}
}
<font face = 黑体>这里为了模拟不同高度,所以我们在Adapter中写了一个返回随机高度的方法,并且在 onBindViewHolder() 方法中判断当前使用的布局是不是瀑布流布局,如果是的话,就使用随机高度。
private int getRandomHeight() {
return (int) (Math.random() * 1000);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
holder.mIv.setImageResource(getIcon(position));
holder.mTv.setText(dataSource.get(position));
/**
* 只在瀑布流布局中使用随机高度
*/
if (mRecyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
holder.mTv.setLayoutParams(params);
} else {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
holder.mTv.setLayoutParams(params);
}
}
5.3、效果展示
<font face = 黑体>具体效果如下所示:
六、插入数据与删除数据
<font face = 黑体>我们在 Adapter 中定义如下添加数据和删除数据的方法,然后在 MainActivity 中调用就可以了,RecyclerView 的添加数据和删除数据会使用默认的动画,当然你也可以自定义动画。这里需要特别注意的是,当添加删除完数据后,一定要调用 notifyItemRangeChanged() 这个方法,去刷新 ItemView,这个方法的第一个参数的意思是刷新的起点,第二个参数是刷新的数量。
/**
* 添加一条数据
* @param position
*/
public void addData(int position) {
addDataPosition = position;
dataSource.add(position, "插入的数据");
notifyItemInserted(position);
notifyItemRangeChanged(position, dataSource.size() - position);
}
/**
* 删除一条数据
* @param position
*/
public void removeData(int position) {
addDataPosition = -1;
dataSource.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, dataSource.size() - position);
}
6.1、效果展示
<font face = 黑体>具体效果如下所示:
七、小结
<font face = 黑体>这篇文章基本上已经将 RecyclerView 的基本使用都讲完了,RecyclerView 的作用是在有限的显示空间里面去展示大量的数据,RecyclerView 本身只负责回收和复用 View,它需要与其他类的配合来达到展示数据的效果,比如说通过配合 Adapter 适配器来把视图与数据连接起来,通过配合 LayoutManager 来确定数据的展示形式(线性、网格、瀑布流),另外 RecyclerView 并没有提供 Item的点击事件响应方法,这个需要我们自己去实现,源码中我已经实现了这个方法,因为比较简单所以上述就没有讲了。
八、源码
<font face = 黑体>源码已经上传至 github,大家直接下载就可以了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。