目录
前言
RecyclerView控件
数据适配器
布局管理器
使用RecyclerView
创建XML布局文件
创建ReActivity类
创建数据适配器
onCreateViewHolder
onBindViewHolder
完整代码
补充知识
ItemDecoration
前言
在前一篇中,我们讲解了ListView控件,那么本篇我们就来讲解RecyclerView控件
RecyclerView控件
与ListView类似,RecyclerView控件同样也是以列表的形式将数据进行展示,都是为了维护少量的View来进行显示大量的数据。是在android5.0之后新添的控件,用来替代传统的ListView和GridView控件的。
与传统的ListView不同,RecyclerView能通过LayoutManager类来实现横向或者竖向的列表效果、瀑布流效果和GridView效果,而ListView只能实现竖直的列表效果。
格式如下:
<androidx.recyclerview.widget.RecyclerViewandroid:layout_width="match_parent"android:layout_height="match_parent"/>
我们 可以通过在Patale中找到RecylcerView,将其拉到XML文件中
当然,我们也可以手动添加:
数据适配器
RecyclerView控件使用的是RecyclerView.Adapter,该数据适配器将BaseAdapter中的getView()方法拆解为onCreateViewHolder()方法和onBindViewHolder()方法,强制使用ViewHolder类,使代码更加规范化。
布局管理器
LayoutManager布局管理器用来设置每一项view在RecyclerView中的位置布局,以及控件item view的显示或隐藏。当View重用或者回收的时候,LayoutManager都会向Adapter来请求新的数据进行替换原来的数据的内容。这种回收复用机制可以提供性能,避免创建很多的view或者是频繁的调用findViewById()方法。
RcyclerView提供了三种内置的LayoutManager:
- LinearLayoutManager:线性布局,横向或者纵向滑动列表
- GridLayoutManager:网格布局
- StaggerdGridLayoutManager:流式布局,如瀑布流效果。
当然,我们也可以通过继承RecyclerView.LayoutManager来实现一个自定义的LayoutManager。
Animations(动画)效果:
- RecyclerView对于Item的添加和删除是默认开启动画的,我们也可以通过RecyclerView.ItemAnimator类来定制动画,再通过RecyclerView.setItemAnimator()方法来使用
以下是RecyclerView的一些相关类:
类名 | 说明 |
RecyclerView.Adapter | 可以托管数据集合,为每一项Item创建视图并且绑定数据 |
RecyclerView.ViewHolder | 承载Item视图的子布局 |
RecyclerView.LayoutManager | 负责Item视图的布局的显示管理 |
RecyclerView.ItemDecoration | 给每一项Item视图添加子View,例如进行画分割线等 |
RecyclerView.ItemAnimator | 负责处理数据添加或者删除时候的动画效果 |
使用RecyclerView
创建XML布局文件
这里我选择创建一个名为recycle_list.xml的布局文件。
这里我们选择模仿聊天的界面,那么首先我们需要实现一个聊天的界面,就在上面创建的布局中实现。效果图如下:
<?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:layout_margin="16dp"><ImageViewandroid:id="@+id/tv"android:src="@mipmap/ic_launcher"android:layout_width="wrap_content"android:layout_height="wrap_content"/><LinearLayoutandroid:layout_width="200dp"android:layout_height="match_parent"android:layout_marginLeft="10dp"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:text="李四"android:textStyle="bold"android:textSize="20sp"android:layout_width="wrap_content"android:layout_height="wrap_content"/><TextViewandroid:id="@+id/news"android:text="李四,你今天在干嘛呢?"android:textSize="15sp"android:layout_marginTop="15sp"android:layout_width="match_parent"android:layout_height="wrap_content"/></LinearLayout><TextViewandroid:id="@+id/tv_time"android:gravity="end"android:text="24/8/25\n12:00"android:textColor="#999999"android:textSize="15sp"android:layout_width="wrap_content"android:layout_weight="1"android:layout_height="match_parent"/>
</LinearLayout>
同时,我们可以创建一个recycle_main.xml布局用来显示聊天界面,在其中放入RecyclerView控件。
<?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="match_parent"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="match_parent"/>
</LinearLayout>
创建ReActivity类
我们既然已经把布局文件做好,那么现在就是有关java的代码了,我们需要创建一个ReActivity类,在里面来进行绑定布局文件等操作。
import android.os.Bundle;import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;public class ReActivity extends AppCompatActivity {private RecyclerView recyclerView;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.recycle_main);Init();}private void Init() {recyclerView = findViewById(R.id.recyclerView);//初始化recyclerView}
}
创建数据适配器
在上面,我们已经把聊天的界面以及RecyclerView控件创建出来了,那么接下来我们就来自定义一个适配器继承RecyclerView.Adapter来创建item view以及绑定数据.
onCreateViewHolder
步骤:
- 创建适配器类继承RecyclerView.Adapter,传入泛型
- 创建内部类即RecyclerView.ViewHolder的子类,并初始化item控件。
- 重写RecyclerView.Adapter类的相关方法。
这里我们使用View.inflate来加载v,并将v传给内部类MyAdapter(自己实现)继续实例化。
private View v;private MyAdapter myAdapter;@NonNull@Overridepublic MyAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {v = View.inflate(ReActivity.this,R.layout.recycle_list,null);myAdapter = new MyAdapter(v);return myAdapter;}
这里创建一个内部类MyAdapter继承ViewHolder,绑定传进来的v的控件。
class MyAdapter extends RecyclerView.ViewHolder {TextView name, news, time;ImageView image;public MyAdapter(@NonNull View itemView) {super(itemView);name = itemView.findViewById(R.id.tv_name);news = itemView.findViewById(R.id.news);time = itemView.findViewById(R.id.tv_time);}}
onBindViewHolder
- 将获取到的数据设置到对应的控件上。
- 传入类的holder和position
position相当于下标,既然需要获取类的数据,那么我们需要一个构造方法来初始化类的数据
private final Context context;private final ArrayList nameList,timeList,newsList;public Adapter(Context context, ArrayList nameList, ArrayList newsList, ArrayList timeList){this.context = context;this.nameList = nameList;this.newsList = newsList;this.timeList = timeList;}
到这,我们已经把自定义的适配器Adapter实现完成,完整代码:
/*** RecyclerView的适配器类*/class Adapter extends RecyclerView.Adapter<Adapter.MyAdapter> {private Context context;private ArrayList nameList, newsList, timeList;/*** Adapter构造方法* @param context 上下文环境,此处为ReActivity实例* @param nameList 名称数据列表* @param newsList 消息数据列表* @param timeList 时间数据列表*/public Adapter(Context context, ArrayList nameList, ArrayList newsList, ArrayList timeList){this.context = context;this.nameList = nameList;this.newsList = newsList;this.timeList = timeList;}/*** 创建ViewHolder* @param parent 父容器,用于容纳RecyclerView项视图* @param viewType 视图类型,此处未使用* @return 创建的ViewHolder实例*/@NonNull@Overridepublic MyAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View v = View.inflate(ReActivity.this,R.layout.recycle_list,null); // 加载RecyclerView项布局文件MyAdapter myAdapter = new MyAdapter(v); // 创建ViewHolderreturn myAdapter;}/*** 绑定ViewHolder数据* @param holder ViewHolder实例,用于展示数据* @param position 数据源中的位置*/@Overridepublic void onBindViewHolder(@NonNull MyAdapter holder, int position) {holder.name.setText(nameList.get(position).toString()); // 设置名称文本holder.news.setText(newsList.get(position).toString()); // 设置消息文本holder.time.setText(timeList.get(position).toString()); // 设置时间文本}/*** 返回数据源中项的数量* @return 数据源大小*/@Overridepublic int getItemCount() {return nameList.size();}/*** RecyclerView ViewHolder子类*/class MyAdapter extends RecyclerView.ViewHolder {TextView name, news, time;ImageView image;/*** MyAdapter构造方法,用于初始化ViewHolder中的视图组件* @param itemView RecyclerView项的根视图*/public MyAdapter(@NonNull View itemView) {super(itemView);name = itemView.findViewById(R.id.tv_name); // 初始化名称文本视图news = itemView.findViewById(R.id.news); // 初始化消息文本视图time = itemView.findViewById(R.id.tv_time); // 初始化时间文本视图}}}
接下来,我们就在我们定义的Init()方法中来实例化这些类以及调用相关的方法。
- 首先实例化存储数据的三个ArrayList,分别存储用户名,信息,时间。并模拟生成一些数据。
- 创建Adapter对象并将上面三个ArratList传入
- 创建线性布局管理器,并给前面的rercyclerView设置布局为线性布局。
- 给rercyclerView设置每一项的动画,并且设置分割线和适配器
/*** 初始化方法,用于初始化RecyclerView组件及适配器*/private void Init() {recyclerView = findViewById(R.id.recyclerView); // 初始化RecyclerView组件// 初始化存储数据的ArrayListArrayList names = new ArrayList();ArrayList newsList = new ArrayList();ArrayList timeList = new ArrayList();// 循环生成模拟数据for(int i=0;i<50;i++){names.add("00"+i);newsList.add("你今天在干嘛呢?");timeList.add("2020-01-01 \n15:"+i);}// 创建Adapter实例并传入数据源Adapter adapter = new Adapter(this ,names,newsList,timeList);// 创建LinearLayoutManager实例,用于管理RecyclerView的布局LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);recyclerView.setLayoutManager(linearLayoutManager); // 设置RecyclerView的布局管理器recyclerView.setItemAnimator(new DefaultItemAnimator()); // 设置RecyclerView的项动画recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); // 添加分割线装饰recyclerView.setAdapter(adapter); // 设置RecyclerView的适配器}
完整代码
ReActivity.java
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import java.util.ArrayList;// ReActivity类继承自AppCompatActivity,用于展示RecyclerView实现的列表界面
public class ReActivity extends AppCompatActivity {// 声明RecyclerView对象private RecyclerView recyclerView;/*** onCreate方法,在活动创建时调用* @param savedInstanceState 可能存在的保存实例状态Bundle,此处为可空*/@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.recycle_main); // 设置活动的布局文件Init(); // 初始化组件和数据}/*** 初始化方法,用于初始化RecyclerView组件及适配器*/private void Init() {recyclerView = findViewById(R.id.recyclerView); // 初始化RecyclerView组件// 初始化存储数据的ArrayListArrayList names = new ArrayList();ArrayList newsList = new ArrayList();ArrayList timeList = new ArrayList();// 循环生成模拟数据for(int i=0;i<50;i++){names.add("00"+i);newsList.add("你今天在干嘛呢?");timeList.add("2020-01-01 \n15:"+i);}// 创建Adapter实例并传入数据源Adapter adapter = new Adapter(this ,names,newsList,timeList);// 创建LinearLayoutManager实例,用于管理RecyclerView的布局LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);recyclerView.setLayoutManager(linearLayoutManager); // 设置RecyclerView的布局管理器recyclerView.setItemAnimator(new DefaultItemAnimator()); // 设置RecyclerView的项动画recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL)); // 添加分割线装饰recyclerView.setAdapter(adapter); // 设置RecyclerView的适配器}/*** RecyclerView的适配器类*/class Adapter extends RecyclerView.Adapter<Adapter.MyAdapter> {private Context context;private ArrayList nameList, newsList, timeList;/*** Adapter构造方法* @param context 上下文环境,此处为ReActivity实例* @param nameList 名称数据列表* @param newsList 消息数据列表* @param timeList 时间数据列表*/public Adapter(Context context, ArrayList nameList, ArrayList newsList, ArrayList timeList){this.context = context;this.nameList = nameList;this.newsList = newsList;this.timeList = timeList;}/*** 创建ViewHolder* @param parent 父容器,用于容纳RecyclerView项视图* @param viewType 视图类型,此处未使用* @return 创建的ViewHolder实例*/@NonNull@Overridepublic MyAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View v = View.inflate(ReActivity.this,R.layout.recycle_list,null); // 加载RecyclerView项布局文件MyAdapter myAdapter = new MyAdapter(v); // 创建ViewHolderreturn myAdapter;}/*** 绑定ViewHolder数据* @param holder ViewHolder实例,用于展示数据* @param position 数据源中的位置*/@Overridepublic void onBindViewHolder(@NonNull MyAdapter holder, int position) {holder.name.setText(nameList.get(position).toString()); // 设置名称文本holder.news.setText(newsList.get(position).toString()); // 设置消息文本holder.time.setText(timeList.get(position).toString()); // 设置时间文本}/*** 返回数据源中项的数量* @return 数据源大小*/@Overridepublic int getItemCount() {return nameList.size();}/*** RecyclerView ViewHolder子类*/class MyAdapter extends RecyclerView.ViewHolder {TextView name, news, time;ImageView image;/*** MyAdapter构造方法,用于初始化ViewHolder中的视图组件* @param itemView RecyclerView项的根视图*/public MyAdapter(@NonNull View itemView) {super(itemView);name = itemView.findViewById(R.id.tv_name); // 初始化名称文本视图news = itemView.findViewById(R.id.news); // 初始化消息文本视图time = itemView.findViewById(R.id.tv_time); // 初始化时间文本视图}}}
}
结合前面的两个XML布局,运行即可得到:
补充知识
ItemDecoration
在使用RecyclerView的时候,如果我们想要设置分割线,那么我们可以通过ItemDecoration。例如:
DividerItemDecoration decoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);分割线recyclerView.addItemDecoration(decoration); // 添加分割线装饰
Item动画
在RecyclerView中提供了默认的IntemAnimator实现类: DefaulttemAnimator。我们可以借助该类帮我们实现一些动画。
DefaultItemAnimator animator = new DefaultItemAnimator();animator.setAddDuration(1000);animator.setRemoveDuration(1000);recyclerView.setItemAnimator(animator); // 设置RecyclerView的项动画
在前面的基础上,在recycle_main.xml聊天界面的上部添加两个按钮(添加和删除)
<LinearLayoutandroid:layout_marginLeft="80dp"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><Buttonandroid:id="@+id/add"android:text="添加Item"android:layout_width="wrap_content"android:layout_height="wrap_content" /><Buttonandroid:id="@+id/del"android:text="删除Item"android:layout_marginLeft="60dp"android:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout>
同时在ReActivity类中添加对应点击事件(由于代码有改动,所以这里直接放全部代码):
package com.example.newapptext1;import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import java.util.ArrayList;// ReActivity类继承自AppCompatActivity,用于展示RecyclerView实现的列表界面
public class ReActivity extends AppCompatActivity implements View.OnClickListener {// 声明RecyclerView对象private RecyclerView recyclerView;private Adapter adapter;private ArrayList names;private ArrayList newsList;private ArrayList timeList;/*** onCreate方法,在活动创建时调用* @param savedInstanceState 可能存在的保存实例状态Bundle,此处为可空*/@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.recycle_main); // 设置活动的布局文件Init(); // 初始化组件和数据}/*** 初始化方法,用于初始化RecyclerView组件及适配器*/private void Init() {recyclerView = findViewById(R.id.recyclerView); // 初始化RecyclerView组件// 初始化存储数据的ArrayListnames = new ArrayList();newsList = new ArrayList();timeList = new ArrayList();// 循环生成模拟数据for(int i=0;i<5;i++){names.add("00"+i);newsList.add("你今天在干嘛呢?");timeList.add("2020-01-01 \n15:"+i);}// 创建Adapter实例并传入数据源adapter = new Adapter(this , names, newsList, timeList);// 创建LinearLayoutManager实例,用于管理RecyclerView的布局LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); // 设置垂直方向recyclerView.setLayoutManager(linearLayoutManager); // 设置RecyclerView的布局管理器DefaultItemAnimator animator = new DefaultItemAnimator();animator.setAddDuration(1000);animator.setRemoveDuration(1000);recyclerView.setItemAnimator(animator); // 设置RecyclerView的项动画DividerItemDecoration decoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);分割线recyclerView.addItemDecoration(decoration); // 添加分割线装饰recyclerView.setAdapter(adapter); // 设置RecyclerView的适配器//添加点击事件findViewById(R.id.add).setOnClickListener(this);findViewById(R.id.del).setOnClickListener(this);}@Overridepublic void onClick(View view) {// 判断点击的视图是否为添加按钮if(view.getId()==R.id.add){// 向列表中添加新的名字和消息,用于模拟新消息的到来names.add(1,"新添");newsList.add(1,"你在干嘛呢?");timeList.add(1,"2020-01-01 \n15:00");// 通知适配器插入新项,以便更新UIadapter.notifyItemInserted(1);} else if(view.getId()==R.id.del){// 判断点击的视图是否为删除按钮// 从名字、消息和时间列表中移除指定位置的元素,以模拟数据删除操作names.remove(1);newsList.remove(1);timeList.remove(1);// 通知适配器移除项,以便更新UIadapter.notifyItemRemoved(1);}}/*** RecyclerView的适配器类*/class Adapter extends RecyclerView.Adapter<Adapter.MyAdapter> {private Context context;private ArrayList nameList, newsList, timeList;/*** Adapter构造方法* @param context 上下文环境,此处为ReActivity实例* @param nameList 名称数据列表* @param newsList 消息数据列表* @param timeList 时间数据列表*/public Adapter(Context context, ArrayList nameList, ArrayList newsList, ArrayList timeList){this.context = context;this.nameList = nameList;this.newsList = newsList;this.timeList = timeList;}/*** 创建ViewHolder* @param parent 父容器,用于容纳RecyclerView项视图* @param viewType 视图类型,此处未使用* @return 创建的ViewHolder实例*/@NonNull@Overridepublic MyAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View v = View.inflate(ReActivity.this,R.layout.recycle_list,null); // 加载RecyclerView项布局文件MyAdapter myAdapter = new MyAdapter(v); // 创建ViewHolderreturn myAdapter;}/*** 绑定ViewHolder数据* @param holder ViewHolder实例,用于展示数据* @param position 数据源中的位置*/@Overridepublic void onBindViewHolder(@NonNull MyAdapter holder, int position) {holder.name.setText(nameList.get(position).toString()); // 设置名称文本holder.news.setText(newsList.get(position).toString()); // 设置消息文本holder.time.setText(timeList.get(position).toString()); // 设置时间文本}/*** 返回数据源中项的数量* @return 数据源大小*/@Overridepublic int getItemCount() {return nameList.size();}/*** RecyclerView ViewHolder子类*/class MyAdapter extends RecyclerView.ViewHolder {TextView name, news, time;ImageView image;/*** MyAdapter构造方法,用于初始化ViewHolder中的视图组件* @param itemView RecyclerView项的根视图*/public MyAdapter(@NonNull View itemView) {super(itemView);name = itemView.findViewById(R.id.tv_name); // 初始化名称文本视图news = itemView.findViewById(R.id.news); // 初始化消息文本视图time = itemView.findViewById(R.id.tv_time); // 初始化时间文本视图}}}
}
运行之后可以得到:
当然,还有个刷新功能,后面再跟大家讲。
以上就是本篇所有内容,若有不足,欢迎指正~