一.简介
RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比ListView更好。原理与ListView原理是类似的:都是仅仅维护少量的View并且可以展示大量的数据集。RecyclerView用以下两种方式简化了数据的展示和处理:
1.使用LayoutManager来确定每一个item的排列方式。
2.为增加和删除项目提供默认的动画效果。
你也可以定义你自己的LayoutManager和添加删除动画,RecyclerView项目结构如下:
Adapter:使用RecyclerView之前,你需要一个继承自RecyclerView.Adapter的适配器,作用是将数据与每一个item的界面进行绑定。
LayoutManager:用来确定每一个item如何进行排列摆放,何时展示和隐藏。回收或重用一个View的时候,LayoutManager会向适配器请求新的数据来替换旧的数据,这种机制避免了创建过多的View和频繁的调用findViewById方法(与ListView原理类似)。
二.基础使用Demo
1.配置Gradle
implementation 'com.android.support:recyclerview-v7:28.0.0'
2.Activity布局
<?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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/colorPrimary"
android:orientation="horizontal"
android:weightSum="3"
tools:ignore="MissingConstraints">
<TextView
android:id="@+id/activity_recyclerview_textview1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="添加数据"
android:textColor="#FFFFFF">
</TextView>
<View
android:layout_width="0.5dp"
android:layout_height="match_parent"
android:background="#FFFFFF">
</View>
<TextView
android:id="@+id/activity_recyclerview_textview2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="删除数据"
android:textColor="#FFFFFF">
</TextView>
<View
android:layout_width="0.5dp"
android:layout_height="match_parent"
android:background="#FFFFFF">
</View>
<TextView
android:id="@+id/activity_recyclerview_textview3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="更新数据"
android:textColor="#FFFFFF">
</TextView>
</LinearLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/activity_recyclerview_srl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="20dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/activity_recyclerview_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
3.Activity
public class RecyclerViewActivity extends AppCompatActivity implements View.OnClickListener {
private RecyclerView mRecyclerView;
private RecycleViewAdapter mRecycleViewAdapter;
private List<RecyclerViewBean> mList;
private TextView mAddTextView;
private TextView mDelTextView;
private TextView mUpdateTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview);
initView();
}
/**
* 初始化各种View
*/
private void initView() {
mAddTextView = findViewById(R.id.activity_recyclerview_textview1);
mDelTextView = findViewById(R.id.activity_recyclerview_textview2);
mUpdateTextView = findViewById(R.id.activity_recyclerview_textview3);
mAddTextView.setOnClickListener(this);
mDelTextView.setOnClickListener(this);
mUpdateTextView.setOnClickListener(this);
mRecyclerView = findViewById(R.id.activity_recyclerview_recyclerView);
mList = getList();
//1.设置LinearLayoutManager ListView
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//2.设置固定大小
mRecyclerView.setHasFixedSize(true);
//3.设置Adapter
mRecycleViewAdapter = new RecycleViewAdapter(this, mList);
mRecyclerView.setAdapter(mRecycleViewAdapter);
//4.RecyclerView点击事件
onRecyclerViewClick();
//5.RecyclerView滚动事件
addOnScrollListener();
}
/**
* RecyclerView点击事件
*/
private void onRecyclerViewClick() {
mRecycleViewAdapter.setRecyclerViewItemClick(new IRecyclerViewItemClick() {
@Override
public void onItemClick(View view, int position) {
Log.d("RecyclerViewActivity", "RecyclerView点击 position----:" + position);
Log.d("RecyclerViewActivity", "RecyclerView点击 title----:" + mList.get(position).getTitle());
}
@Override
public void onItemLongClick(View view, int position) {
Log.d("RecyclerViewActivity", "RecyclerView长按 position----:" + position);
Log.d("RecyclerViewActivity", "RecyclerView长按 title----:" + mList.get(position).getTitle());
}
});
}
/**
* 滚动监听
*/
private void addOnScrollListener() {
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case SCROLL_STATE_SETTLING://滚动
break;
case SCROLL_STATE_DRAGGING://拖动
break;
case SCROLL_STATE_IDLE://空闲
break;
default://其他
break;
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
}
/**
* 初始化集合数据
*/
private List<RecyclerViewBean> getList() {
List<RecyclerViewBean> list = new ArrayList<>();
RecyclerViewBean listViewBean1 = new RecyclerViewBean();
listViewBean1.setAva("");
listViewBean1.setTitle("张三");
RecyclerViewBean listViewBean2 = new RecyclerViewBean();
listViewBean2.setAva("");
listViewBean2.setTitle("韦德");
RecyclerViewBean listViewBean3 = new RecyclerViewBean();
listViewBean3.setAva("");
listViewBean3.setTitle("保罗");
for (int i = 0; i < 10; i++) {
list.add(listViewBean1);
list.add(listViewBean2);
list.add(listViewBean3);
}
return list;
}
/**
* 各种点击事件
*/
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.activity_recyclerview_textview1) {//添加数据
RecyclerViewBean recyclerViewBean = new RecyclerViewBean();
recyclerViewBean.setTitle("添加的数据");
mRecycleViewAdapter.addData(recyclerViewBean, 2);
} else if (id == R.id.activity_recyclerview_textview2) {//删除数据
mRecycleViewAdapter.removeData(2);
} else if (id == R.id.activity_recyclerview_textview3) {//更新数据
RecyclerViewBean recyclerViewBean = mList.get(3);
recyclerViewBean.setTitle("单条数据更新");
mRecycleViewAdapter.updateData(recyclerViewBean, 3);
}
}
}
4.Adapter
public class RecycleViewAdapter extends RecyclerView.Adapter<RecycleViewAdapter.ViewHolder> {
private IRecyclerViewItemClick mClick;
public void setRecyclerViewItemClick(IRecyclerViewItemClick click) {
mClick = click;
}
private List<RecyclerViewBean> mList;
private LayoutInflater mInflater;
public RecycleViewAdapter(Context context, List<RecyclerViewBean> list) {
mList = list;
mInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recyclerview_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.textView.setText(mList.get(position).getTitle());
//Item点击事件
holder.itemView.setOnClickListener(v -> {
if (null != mClick) {
mClick.onItemClick(v, position);
}
});
//Item长按事件
holder.itemView.setOnLongClickListener(v -> {
if (null != mClick) {
mClick.onItemLongClick(v, position);
}
return true;
});
}
@Override
public int getItemCount() {
return mList.size();
}
/**
* ViewHolder类
*/
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
public ViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.recyclerview_item_textview);
}
}
/**
* 刷新数据 全量
*/
public void updateData(List<RecyclerViewBean> list) {
if (null == list || list.size() == 0) return;
mList = list;
notifyDataSetChanged();
}
/**
* 添加数据
*/
public void addData(RecyclerViewBean recyclerViewBean, int position) {
mList.add(position, recyclerViewBean);//集合中添加数据
notifyItemInserted(position);//Adapter添加数据 内部调用notifyItemRangeInserted(position, 1)
notifyItemRangeChanged(position, mList.size());//Adapter数据变化范围 不添加此行代码 也能添加 但是点击时可能错乱
}
/**
* 删除数据
*/
public void removeData(int position) {
mList.remove(position);//集合中删除数据
notifyItemRemoved(position);//Adapter删除数据 内部调用notifyItemRangeRemoved(position, 1)
notifyItemRangeChanged(position, mList.size());//Adapter数据变化范围 不添加此行代码点 也能删除 但是击时可能错乱
}
/**
* 更新数据
* 这种方式更新一个Item其实是有问题的 尤其是列表中数据比较多时,可能会造成Item错乱
*/
public void updateData(RecyclerViewBean recyclerViewBean, int position) {
mList.set(position, recyclerViewBean);//集合中更新数据
notifyItemChanged(position);//Adapter更新数据 内部调用notifyItemRangeChanged(position, 1)
}
}
5. 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:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="90dp">
<ImageView
android:id="@+id/recyclerview_item_imageview"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
android:scaleType="fitXY"
android:src="@color/colorPrimary"/>
<TextView
android:id="@+id/recyclerview_item_textview"
android:layout_width="wrap_content"
android:layout_height="70dp"
android:gravity="center|left"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/recyclerview_item_imageview"
android:layout_marginLeft="15dp"
android:text="张三"/>
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#88CCCCCC">
</View>
</LinearLayout>
6.接口
public interface IRecyclerViewItemClick {
/**
* Item点击事件
*
* @param view View
* @param position 位置
*/
void onItemClick(View view, int position);
/**
* Item长按事件
*
* @param view View
* @param position 位置
*/
void onItemLongClick(View view, int position);
}
三.具体知识讲解
1.包
RecycleView所在包android.support.v7.widget.RecyclerView。
2.Adapter
和ListView直接继承BaseAdapter不同。RecycleView的适配器
继承RecyclerView.Adapter<XXXAdapter.ViewHolder>。然后至少需要重写三个方法。
onCreateViewHolder方法:引入Item布局。
onBindViewHolder方法:获取数值。
getItemCount方法:获取列表Item条数。
3.Item点击
与ListView不同,RecycleView没有直接提供Item点击以及Item长按事件。需要自己写。详解见上面Demo。
4. 删除和添加数据
RecycleView 删除 添加 单条数据 非常方便。
Adapter中
/**
* 添加数据
*/
public void addData(RecyclerViewBean recyclerViewBean, int position) {
mList.add(position, recyclerViewBean);//集合中添加数据
notifyItemInserted(position);//Adapter添加数据 内部调用notifyItemRangeInserted(position, 1)
notifyItemRangeChanged(position, mList.size());//Adapter数据变化范围 不添加此行代码 也能添加 但是点击时可能错乱
}
/**
* 删除数据
*/
public void removeData(int position) {
mList.remove(position);//集合中删除数据
notifyItemRemoved(position);//Adapter删除数据 内部调用notifyItemRangeRemoved(position, 1)
notifyItemRangeChanged(position, mList.size());//Adapter数据变化范围 不添加此行代码点 也能删除 但是击时可能错乱
}
备注
RecyclerView所有的刷新Item方法以及说明
notifyItemChanged(int)//内部调用notifyItemRangeChanged(position, 1) 更新单条Item时使用 但是问题比较多 尤其是列表中数据比较多时 所以局部刷新 不能使用这种方式 详解见下面
notifyItemInserted(int)//插入一条数据 内部调用notifyItemRangeInserted(position, 1) 一般结合notifyItemRangeChanged方法使用 即插入指定位置的数据后通知 从该位置到集合的底部 数据发生改变
notifyItemRemoved(int)//删除一条数据 内部调用notifyItemRangeRemoved(position, 1) 一般结合notifyItemRangeChanged方法使用 即删除指定位置的数据后通知 从该位置到集合的底部 数据发生改变
notifyItemRangeChanged(int, int)//数据发生改变 即 删除指定位置的数据 或者插入指定位置的数据 后通知 从该位置到集合的底部 数据发生改变
notifyItemRangeInserted(int, int)//notifyItemInserted(int)方法内部调用
notifyItemRangeRemoved(int, int)//notifyItemRemoved(int)方法内部调用
添加单条数据,删除单条数据。RecyclerView给我们提供了局部的刷新方法。但是更新一个Item或者集合中有几个Bean发生变化时可能会有问题。
局部刷新详解
RecycleView实现下拉刷新(DiffUtil工具实现局部刷新)
https://blog.csdn.net/weixin_37730482/article/details/71173037
5.滚动事件
RecycleView的滚动事件最好是这样添加。
recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener())
因为
recyclerview.setOnScrollListener(new RecyclerView.OnScrollListener())
可能会出现空指针,且已过期。另外RecycleView滚动事件 状态
SCROLL_STATE_SETTLING:滚动
SCROLL_STATE_DRAGGING:拖动
SCROLL_STATE_IDLE:空闲
6.LayoutManager
目前SDK中提供了三种自带的LayoutManager
LinearLayoutManager:ListView列表
GridLayoutManager:GridView列表
StaggeredGridLayoutManager:纵向列表
recyclerview.setLayoutManager(new LinearLayoutManager(this));
recyclerview.setLayoutManager(new GridLayoutManager(GlideRecycleViewActivity.this, 4));//4:GridView一行显示几个Item
recyclerview.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.HORIZONTAL));2:纵向列表一页显示几行item
7.动画效果
可设置RecycleView的Animator(动画效果,这里以默认为例)
recyclerview.setItemAnimator(new DefaultItemAnimator());
自定义动画
https://blog.csdn.net/weixin_37730482/article/details/72822878
8.RecyclerView的Item隐藏
需求中,可能有这样的需求。就是RecyclerView列表中可能某一个Item需要隐藏不展示。如果仅仅使用传统的GONE的用法,可能会出现空白的问题。
public class RecycleViewAdapter extends RecyclerView.Adapter<RecycleViewAdapter.ViewHolder> {
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.textView.setText(mList.get(position).getTitle());
//Item点击事件
holder.itemView.setOnClickListener(v -> {
if (null != mClick) {
mClick.onItemClick(v, position);
}
});
//Item长按事件
holder.itemView.setOnLongClickListener(v -> {
if (null != mClick) {
mClick.onItemLongClick(v, position);
}
return true;
});
//模拟Item隐藏与否
if (position == 5) {
setRecyclerViewItemVisibility(holder.itemView, false);
}
}
/**
* Item隐藏显示
*/
public void setRecyclerViewItemVisibility(View itemView, boolean isVisible) {
RecyclerView.LayoutParams param = (RecyclerView.LayoutParams) itemView.getLayoutParams();
if (isVisible) {
param.height = 100;//具体值看具体需求
param.width = LinearLayout.LayoutParams.MATCH_PARENT;//具体值看具体需求
itemView.setVisibility(View.VISIBLE);
} else {
itemView.setVisibility(View.GONE);
param.height = 0;
param.width = 0;
}
itemView.setLayoutParams(param);
}
}
9.Demo