題外話:3月初的時(shí)候
RecyclerView使用方法總結(jié)開始被我公開在GitHub上,前后反反復(fù)復(fù)的添加修改將自己對RecyclerView的認(rèn)識(shí)全面的寫出來。網(wǎng)上也有很多RecyclerView的開源庫,如果你嫌麻煩可以直接用那些大神寫的庫,但是隨著Android版本的升級(jí)那些開源庫如果不更新就會(huì)出現(xiàn)大大小小的問題,到時(shí)候會(huì)很煩。我總結(jié)的方法是最原生最Android的,從根上告訴你這個(gè)地方應(yīng)該怎樣去實(shí)現(xiàn)。
官方指南:
https://developer.android.google.cn/guide/topics/ui/layout/recyclerview.html
示例項(xiàng)目已上傳至GitHub(如果對你有幫助請Star,謝謝):
https://github.com/cnwutianhao/RecyclerView
文章分三大部分:入門篇、進(jìn)階篇、拓展篇
導(dǎo)入必要的庫:
implementation 'com.android.support:recyclerview-v7:27.1.0'
一、入門篇
- 縱向布局
- 橫向布局
- 網(wǎng)格布局
1. 縱向布局
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/vertical
示例圖:

2. 橫向布局
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/horizontal
示例圖:

3. 網(wǎng)格布局
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/grid
示例圖:

總結(jié):
縱向布局、橫向布局、網(wǎng)格布局的item可以說相差微乎其微,關(guān)鍵區(qū)別在于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);
網(wǎng)格布局
recyclerview.setLayoutManager(new GridLayoutManager(this, 橫排數(shù)量));
或
GridLayoutManager manager = new GridLayoutManager(this, 橫排數(shù)量);
recyclerview.setLayoutManager(manager);
二、進(jìn)階篇
- 點(diǎn)擊
- 分組
- 頂部懸?。ㄎ敚?/li>
- 拖動(dòng)
- 滑動(dòng)刪除
- 下拉刷新
- 上拉加載
- 雙向滑動(dòng)
- 居中對齊
- 展開和收縮
- 瀑布流
- 時(shí)間軸
- 添加 Footer(包含 List 樣式 和 網(wǎng)格樣式)
- 添加 Header(包含 List 樣式 和 網(wǎng)格樣式)
1. 點(diǎn)擊
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/click
示例圖:

總結(jié):
RecyclerView的點(diǎn)擊事件有兩種寫法:
① 在Adapter里面直接對控件做點(diǎn)擊事件
② 寫接口,在Activity或Fragment上實(shí)現(xiàn)接口中定義的方法
在 Adapter 里面直接對控件做點(diǎn)擊事件
@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(參數(shù)類型 參數(shù)名 , ...);
}
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 上實(shí)現(xiàn)接口中定義的方法
public class XxxActivity extends AppCompatActivity implements XxxAdapter.OnItemClickListener
XxxAdapter adapter = new XxxAdapter(context, this);
@Override
public void onItemClick(String content) {
// TODO:
}
2. 分組
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/group
示例圖:

總結(jié):
RecyclerView分組的寫法有很多種,我會(huì)在分組、頂部懸浮、聯(lián)動(dòng)中給出不同的寫法。
第一種分組寫法也是最簡單的一種,將組名和內(nèi)容寫在同一個(gè)布局,在onBindViewHolder里根據(jù)位置來進(jìn)行組名的顯示與隱藏:
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. 頂部懸?。ㄎ敚?/strong>
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/sticky
示例圖:

總結(jié):
頂部懸浮也涉及到了分組,第二種分組寫法將組名和RecyclerView同級(jí)寫,item里面組名和內(nèi)容寫法與之前的分組類似,然后在onBindViewHolder里判斷是否是第一個(gè)頭部,有無頭部來進(jìn)行判斷:
組名和 RecyclerView 同級(jí)寫
<?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 里面寫組名和內(nèi)容
<?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>
判斷是否是第一個(gè)頭部,有無頭部來進(jìn)行判斷
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(實(shí)體類.組);
holder.itemView.setTag(FIRST_STICKY_VIEW);
} else {
if (!TextUtils.equals(實(shí)體類.組, mList.get(position - 1).組)) {
holder.組名.setVisibility(View.VISIBLE);
holder.組名.setText(實(shí)體類.組);
holder.itemView.setTag(HAS_STICKY_VIEW);
} else {
holder.組名.setVisibility(View.GONE);
holder.itemView.setTag(NONE_STICKY_VIEW);
}
}
4. 拖動(dòng)
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/drag
示例圖:

總結(jié):
關(guān)鍵字:ItemTouchHelper
5. 滑動(dòng)刪除
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/swipe
示例圖:

總結(jié):
關(guān)鍵字:ItemTouchHelper
6. 下拉刷新
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/refresh
示例圖:

總結(jié):
關(guān)鍵字:swipeRefreshLayout.setOnRefreshListener
7. 上拉加載
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/load
示例圖:

總結(jié):
關(guān)鍵字:recyclerview.addOnScrollListener,在onScrollStateChanged里判斷RecyclerView的狀態(tài)是空閑時(shí),同時(shí)是最后一個(gè)可見的item時(shí)才加載,在onScrolled里獲取最后一個(gè)可見的item。
8. 雙向滑動(dòng)
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/slide
示例圖:

總結(jié):
在Adapter的getItemViewType里判斷是哪種布局,在onBindViewHolder里分別對應(yīng)自己的ViewHolder。
9. 居中對齊
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/snaphelper
示例圖:

總結(jié):
關(guān)鍵字:SnapHelper
10. 展開和收縮
示例圖:

總結(jié):
在item里將主內(nèi)容和副內(nèi)容寫出來,通關(guān)點(diǎn)擊item副內(nèi)容現(xiàn)實(shí)和隱藏來達(dá)到效果:
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. 瀑布流
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/waterfall
示例圖:

總結(jié):
關(guān)鍵字:StaggeredGridLayoutManager,示例代碼取的是干貨集中營福利的接口數(shù)據(jù)。
12. 時(shí)間軸
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/timeline
示例圖:

總結(jié):
在item里寫兩種狀態(tài)的布局,一種是當(dāng)前狀態(tài)的,另一種是之前狀態(tài)的。在onBindViewHolder里通過判斷位置現(xiàn)實(shí)和隱藏來實(shí)現(xiàn):
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 樣式 和 網(wǎng)格樣式)
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/footer
示例圖:


總結(jié):
給RecyclerView添加底部有兩種形式,一種是List型,另一種是網(wǎng)格型。關(guān)鍵代碼是在getItemViewType里判斷位置,然后在onCreateViewHolder里面顯示不同的布局。網(wǎng)格布局還用到了setSpanSizeLookup。
13. 添加 Header(包含 List 樣式 和 網(wǎng)格樣式)
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/header
示例圖:


總結(jié):
與添加底部類似。
三、拓展篇
- 聯(lián)動(dòng)
左右聯(lián)動(dòng)
示例項(xiàng)目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/link
示例圖:

總結(jié):
參考 GangedRecyclerview,但是原著的代碼對于那些理解RecyclerView不透徹的不是很友好,都是自定義類,上手難,我對其進(jìn)行了修改,去掉所有的自定義類,達(dá)到標(biāo)準(zhǔn)的書寫格式,代碼看上去很安卓。