【 Android 】RecyclerView 使用方法总结
题外话:3月初的时候
RecyclerView
使用方法总结开始被我公开在GitHub
上,前后反反复复的添加修改将自己对RecyclerView
的认识全面的写出来。网上也有很多RecyclerView
的开源库,如果你嫌麻烦可以直接用那些大神写的库,但是随着Android
版本的升级那些开源库如果不更新就会出现大大小小的问题,到时候会很烦。我总结的方法是最原生最Android
的,从根上告诉你这个地方应该怎样去实现。
官方指南:
https://developer.android.google.cn/guide/topics/ui/layout/recyclerview.html
示例项目已上传至GitHub
(如果对你有帮助请Star
,谢谢):
https://github.com/cnwutianhao/RecyclerView
文章分三大部分:入门篇、进阶篇、拓展篇
导入必要的库:
implementation 'com.android.support:recyclerview-v7:27.1.0'
一、入门篇
- 纵向布局
- 横向布局
- 网格布局
1. 纵向布局
示例图:
纵向布局.gif
2. 横向布局
示例图:
横向布局.gif
3. 网格布局
示例图:
网格布局.gif
总结:
纵向布局、横向布局、网格布局的item
可以说相差微乎其微,关键区别在于setLayoutManager()
的写法不同:
纵向布局
recyclerview.setLayoutManager(new LinearLayoutManager(this));
或
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerview.setLayoutManager(manager);
横向布局
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerview.setLayoutManager(manager);
网格布局
recyclerview.setLayoutManager(new GridLayoutManager(this, 横排数量));
或
GridLayoutManager manager = new GridLayoutManager(this, 横排数量);
recyclerview.setLayoutManager(manager);
二、进阶篇
- 点击
- 分组
- 顶部悬浮(吸顶)
- 拖动
- 滑动删除
- 下拉刷新
- 上拉加载
- 双向滑动
- 居中对齐
- 展开和收缩
- 瀑布流
- 时间轴
- 添加 Footer(包含 List 样式 和 网格样式)
- 添加 Header(包含 List 样式 和 网格样式)
1. 点击
示例图:
点击.gif
总结:
RecyclerView
的点击事件有两种写法:
① 在Adapter
里面直接对控件做点击事件
② 写接口,在Activity
或Fragment
上实现接口中定义的方法
在 Adapter 里面直接对控件做点击事件
@Override
public void onBindViewHolder(@NonNull XxxViewHolder holder, int position) {
holder.控件名.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO:
}
});
}
在 Adapter 里写接口
public interface OnItemClickListener {
void onItemClick(参数类型 参数名 , ...);
}
private OnItemClickListener mListener;
public XxxAdapter(Context context, OnItemClickListener listener) {
mContext = context;
mListener = listener;
}
@Override
public void onBindViewHolder(@NonNull XxxViewHolder holder, int position) {
holder.控件名.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.onItemClick(content);
}
});
}
在 Activity 或 Fragment 上实现接口中定义的方法
public class XxxActivity extends AppCompatActivity implements XxxAdapter.OnItemClickListener
XxxAdapter adapter = new XxxAdapter(context, this);
@Override
public void onItemClick(String content) {
// TODO:
}
2. 分组
示例图:
分组.gif
总结:
RecyclerView
分组的写法有很多种,我会在分组
、顶部悬浮
、联动
中给出不同的写法。
第一种分组写法也是最简单的一种,将组名和内容写在同一个布局,在onBindViewHolder
里根据位置来进行组名的显示与隐藏:
if (position == 0) {
holder.组名.setVisibility(View.VISIBLE);
} else {
if (list.get(position).get组().equals(list.get(position - 1).get组())) {
holder.组名.setVisibility(View.GONE);
} else {
holder.组名.setVisibility(View.VISIBLE);
}
}
3. 顶部悬浮(吸顶)
示例图:
顶部悬浮.gif
总结:
顶部悬浮也涉及到了分组,第二种分组写法将组名和RecyclerView
同级写,item
里面组名和内容写法与之前的分组类似,然后在onBindViewHolder
里判断是否是第一个头部,有无头部来进行判断:
组名和 RecyclerView 同级写
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rcv_sticky"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
android:scrollbars="none" />
<include layout="@layout/sticky_include_recycle_item" />
</RelativeLayout>
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">
<include layout="@layout/sticky_include_recycle_item" />
<TextView
android:id="@+id/tv_team"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="@string/app_name" />
</LinearLayout>
判断是否是第一个头部,有无头部来进行判断
public static final int FIRST_STICKY_VIEW = 1;
public static final int HAS_STICKY_VIEW = 2;
public static final int NONE_STICKY_VIEW = 3;
if (position == 0) {
holder.组名.setVisibility(View.VISIBLE);
holder.组名.setText(实体类.组);
holder.itemView.setTag(FIRST_STICKY_VIEW);
} else {
if (!TextUtils.equals(实体类.组, mList.get(position - 1).组)) {
holder.组名.setVisibility(View.VISIBLE);
holder.组名.setText(实体类.组);
holder.itemView.setTag(HAS_STICKY_VIEW);
} else {
holder.组名.setVisibility(View.GONE);
holder.itemView.setTag(NONE_STICKY_VIEW);
}
}
4. 拖动
示例图:
拖动.gif
总结:
关键字:ItemTouchHelper
5. 滑动删除
示例图:
滑动删除.gif
总结:
关键字:ItemTouchHelper
6. 下拉刷新
示例图:
下拉刷新.gif
总结:
关键字:swipeRefreshLayout.setOnRefreshListener
7. 上拉加载
示例图:
上拉加载.gif
总结:
关键字:recyclerview.addOnScrollListener
,在onScrollStateChanged
里判断RecyclerView
的状态是空闲时,同时是最后一个可见的item
时才加载,在onScrolled
里获取最后一个可见的item
。
8. 双向滑动
示例图:
双向滑动.gif
总结:
在Adapter
的getItemViewType
里判断是哪种布局,在onBindViewHolder
里分别对应自己的ViewHolder
。
9. 居中对齐
示例图:
居中对齐.gif
总结:
关键字:SnapHelper
10. 展开和收缩
示例图:
展开和收缩.gif
总结:
在item
里将主内容和副内容写出来,通关点击item
副内容现实和隐藏来达到效果:
private int expandedPosition = -1;
final boolean isExpanded = position == expandedPosition;
holder.rlChild.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.rlParent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mViewHolder != null) {
mViewHolder.rlChild.setVisibility(View.GONE);
notifyItemChanged(expandedPosition);
}
expandedPosition = isExpanded ? -1 : holder.getAdapterPosition();
mViewHolder = isExpanded ? null : holder;
notifyItemChanged(holder.getAdapterPosition());
}
});
11. 瀑布流
示例图:
瀑布流.gif
总结:
关键字:StaggeredGridLayoutManager
,示例代码取的是干货集中营福利的接口数据。
12. 时间轴
示例图:
时间轴.gif
总结:
在item
里写两种状态的布局,一种是当前状态的,另一种是之前状态的。在onBindViewHolder
里通过判断位置现实和隐藏来实现:
private static final int TYPE_HEADER = 0;
private static final int TYPE_NORMAL = 1;
if (getItemViewType(position) == TYPE_HEADER) {
holder.tvHeaderLine.setVisibility(View.INVISIBLE);
holder.tvTime.setTextColor(Color.BLACK);
holder.tvContext.setTextColor(Color.BLACK);
holder.tvDot.setBackgroundResource(R.drawable.timeline_dot_header);
} else if (getItemViewType(position) == TYPE_NORMAL) {
holder.tvHeaderLine.setVisibility(View.VISIBLE);
holder.tvTime.setTextColor(Color.GRAY);
holder.tvContext.setTextColor(Color.GRAY);
holder.tvDot.setBackgroundResource(R.drawable.timeline_dot_normal);
}
13. 添加 Footer(包含 List 样式 和 网格样式)
示例图:
为 RecyclerView 添加 Footer(List 样式).gif
为 RecyclerView 添加 Footer(网格样式).gif
总结:
给RecyclerView
添加底部有两种形式,一种是List
型,另一种是网格型
。关键代码是在getItemViewType
里判断位置,然后在onCreateViewHolder
里面显示不同的布局。网格布局还用到了setSpanSizeLookup
。
13. 添加 Header(包含 List 样式 和 网格样式)
示例图:
为 RecyclerView 添加 Header(List 样式).gif
为 RecyclerView 添加 Header(网格样式).gif
总结:
与添加底部类似。
三、拓展篇
- 联动
左右联动
示例图:
左右联动.gif
总结:
参考 GangedRecyclerview,但是原著的代码对于那些理解RecyclerView
不透彻的不是很友好,都是自定义类,上手难,我对其进行了修改,去掉所有的自定义类,达到标准的书写格式,代码看上去很安卓。