RecyclerView下拉刷新上拉加载

背景

作为ListViewGridView的替代者, RecyclerView以它的灵活性著称,而且有着更好更完善的缓存处理机制。但是在使用RecyclerVew的过程中有个很明显的问题:RecyclerView没有为开发者提供addHeadViewaddFooterView两个接口,这就为RecyclerView的下拉刷新和上拉加载实现增加了难度,本文就为大家介绍RecyclerView的下拉刷新和上拉加载的实现方式。

下拉刷新

其实RecyclerView的下拉刷新使用SwipeRefreshLayout组件实现还是非常简单的。

SwipeRefreshLayout也是Android SDK中为我们提供的一个布局容器类,它作为容器可以为它的子元素提供下拉刷新接口,而且使用方式也非常简单,我就直接上代码了

1
2
3
4
5
6
7
8
9
10
11
12
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>

这就是RecyclerView结合SwipeRefreshLayout实现下拉刷新的布局方式,非常简单吧,写完布局文件之后,当然还要处理刷新的逻辑

1
2
3
4
5
6
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//在此处实现刷新获取数据,然后更新RecyclerView的数据源即可
}
});

写完这两段代码之后,RecyclerView的下拉刷新就实现完成了,是不是很简单。不过还有个小小的问题需要注意下,那就是在刷新获取数据处理逻辑结束之后需要调用

1
mSwipeRefreshLayout.setRefreshing(false);

这个接口就是告诉SwipRefreshLayout刷新动作已经结束,刷新进度条可以不用再显示了,否则的话视图顶部的刷新进度条会一直存在,当然如果你手动调用

1
mSwipeRefreshLayout.setRefreshing(true);

这个接口的话,刷新进度条就会显示出来。

上拉加载

由于RecyclerView中没有提供addFooterView接口,所以我们没有办法像ListView中那样直接添加一个FooterView作为上拉加载的视图。但是RecyclerView.Adapter中也提供了public int getItemViewType(int position)接口,我们可以为RecyclerView.Adapter指定两种ViewType,一个作为普通的ItemViewType用来显示普通数据视图,一个作为FooterViewType用来显示上拉加载视图,这样的话我们就需要在展示数据的基础上为RecyclerView.Adapter额外增加一条数据用来显示添加的FooterView,这个特定Adapter的实现逻辑如下:

  1. 定义两个ViewType分表用来区分普通是数据视图和上拉加载视图

    1
    2
    private static final int VIEW_TYPE_FOOTER = 0;
    private static final int VIEW_TYPE_ITEM = 1;
  2. 重写getItemCount()方法,在原有数据的基础上增加一条数据用来显示FooterView

    1
    2
    3
    4
    5
    @Override
    public int getItemCount() {
    return mList.size() + 1;//在原有数据的基础增加一个数据用来显示FooterView
    }
  3. 重写getItemViewType(int position)方法,用来判断当前加载显示何种视图

    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    public int getItemViewType(int position) {
    if(position + 1 == getItemCount()) {//最后一条数据显示FooterView
    return VIEW_TYPE_FOOTER;
    }
    return VIEW_TYPE_ITEM;
    }
  4. onCreateViewHolder方法中根据viewType初始化对应的视图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if(viewType == VIEW_TYPE_FOOTER) {
    return onCreateFooterViewHolder(parent, viewType);
    } else if(viewType == VIEW_TYPE_ITEM) {
    return onCreateItemViewHolder(parent, viewType);
    }
    return null;
    }
  5. onBindViewHolder方法中根据viewType绑定显示对应的视图数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    switch (getItemViewType(position)) {
    case VIEW_TYPE_ITEM:
    onBindItemViewHolder(holder, position);
    break;
    case VIEW_TYPE_FOOTER:
    onBindFooterViewHolder(holder, position, mLoadStatus);
    break;
    default:
    break;
    }
    }

整个Adapter完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
public class DemoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private LoadStatus mLoadStatus = LoadStatus.CLICK_LOAD_MORE;//上拉加载的状态
private static final int VIEW_TYPE_FOOTER = 0;
private static final int VIEW_TYPE_ITEM = 1;
private List<String> mList;
private Context mContext;
public DemoAdapter(Context context, List<String> list) {
mContext = context;
mList = list;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == VIEW_TYPE_FOOTER) {
return onCreateFooterViewHolder(parent, viewType);
} else if(viewType == VIEW_TYPE_ITEM) {
return onCreateItemViewHolder(parent, viewType);
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case VIEW_TYPE_ITEM:
onBindItemViewHolder(holder, position);
break;
case VIEW_TYPE_FOOTER:
onBindFooterViewHolder(holder, position, mLoadStatus);
break;
default:
break;
}
}
public RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(mContext, R.layout.footer_layout, null);
return new FooterViewHolder(view);
}
public RecyclerView.ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(mContext, R.layout.item_layout, null);
return new ItemViewHolder(view);
}
public void onBindFooterViewHolder(RecyclerView.ViewHolder holder, int position, LoadStatus loadStatus) {
FooterViewHolder viewHolder = (FooterViewHolder) holder;
switch (loadStatus) {
case CLICK_LOAD_MORE:
viewHolder.mLoadingLayout.setVisibility(View.GONE);
viewHolder.mClickLoad.setVisibility(View.VISIBLE);
break;
case LOADING_MORE:
viewHolder.mLoadingLayout.setVisibility(View.VISIBLE);
viewHolder.mClickLoad.setVisibility(View.GONE);
break;
}
}
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
ItemViewHolder viewHolder = (ItemViewHolder) holder;
viewHolder.mTextView.setText(getItem(position));
}
@Override
public int getItemCount() {
return mList.size() + 1;
}
public String getItem(int position) {
return mList.get(position);
}
public void addAll(List<String> list) {
this.mList.addAll(list);
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if(position + 1 == getItemCount()) {//最后一条为FooterView
return VIEW_TYPE_FOOTER;
}
return VIEW_TYPE_ITEM;
}
public void reSetData(List<String> list) {
this.mList = list;
notifyDataSetChanged();
}
public void setLoadStatus(LoadStatus loadStatus) {
this.mLoadStatus = loadStatus;
}
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ItemViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.textView);
}
}
public class FooterViewHolder extends RecyclerView.ViewHolder {
public LinearLayout mLoadingLayout;
public TextView mClickLoad;
public FooterViewHolder(View itemView) {
super(itemView);
mLoadingLayout = (LinearLayout) itemView.findViewById(R.id.loading);
mClickLoad = (TextView) itemView.findViewById(R.id.click_load_txt);
mClickLoad.setOnClickListener(new View.OnClickListener() {//添加点击加载更多监听
@Override
public void onClick(View v) {
loadMore();
}
});
}
}
public enum LoadStatus {
CLICK_LOAD_MORE,//点击加载更多
LOADING_MORE//正在加载更多
}

FooterView的布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center_horizontal"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/loading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="正在加载..."/>
</LinearLayout>
<TextView
android:id="@+id/click_load_txt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:text="点击加载更多"
android:visibility="gone"/>
</RelativeLayout>

  1. 写完AdapterFooterView视图适配之后,我们还有一个重要的步骤需要做,在哪里实现加载更多的逻辑处理

其实RecyclerView也为我们提供了mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() )接口,这样我们就看一看通过这个接口来判断什么时候调用加载更多的实现接口,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE
&& mLastVisibleItemPosition + 1 == mAdapter.getItemCount()) {
loadMore();
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mLastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
}
});

整个DemoActivity的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private SwipeRefreshLayout mSwipeRefreshLayout;
private DemoAdapter mAdapter;
private int mLastVisibleItemPosition = 0;
private LinearLayoutManager mLayoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter = new DemoAdapter(this, getData("init"));
mRecyclerView.setAdapter(mAdapter);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000);
final List<String> list = getData("refresh");
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.reSetData(list);
mSwipeRefreshLayout.setRefreshing(false);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
});
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE
&& mLastVisibleItemPosition + 1 == mAdapter.getItemCount()) {
loadMore();
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mLastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
}
});
}
public void loadMore() {
mAdapter.setLoadStatus(LoadStatus.LOADING_MORE);
new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000);
final List<String> list = getData("load more");
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.addAll(list);
mAdapter.setLoadStatus(LoadStatus.CLICK_LOAD_MORE);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
public List<String> getData(String flag) {
int idx = 1;
if(mAdapter != null) {
idx = mAdapter.getItemCount();
}
List<String> list = new ArrayList<>(10);
for(int i = 0; i < 10; i++) {
list.add(flag + ":" + (idx + i));
}
return list;
}
}

以上就是我针对RecyclerView的下拉刷新上拉加载的实现方式。


原创文章,本文采用知识共享署名 2.5(中国大陆许可协议)进行许可,欢迎转载,但转载请注明来自ittiger.cn,并保证转载后文章内容的完整性。本人(laohu)保留所有版权相关权利。



评论