今天研究了下RecyclerView的滑动事件,特别是下拉刷新和加载更多事件,在现在几乎所有的APP显示数据列表时都用到了。自定义RecyclerView下拉刷新和加载更多听上去很复杂,实际上并不难,只要是对滑动事件的监听和处理。
一、自定义RecyclerView实现下拉刷新和加载更多
1、如何判断RecyclerView是在上滑还是下滑
在RecyclerView的OnScrollListener滑动事件监听中有个好用的方法,就是onScrolled(RecyclerView recyclerView, int dx, int dy),其中根据dx的值的正负就可以判断是在左滑还是右滑,而根据dy的值就可以判断是在上滑还是下滑。
1 //上滑
2 if(dy>0){
3 //相应操作代码
4 }
5 //下滑
6 else if(dy<0){
7 //相应操作代码
8 }
2、如何判断是否滑到了顶部或者底部
同样在RecyclerView的OnScrollListener滑动事件监听中onScrolled(RecyclerView recyclerView, int dx, int dy)方法中处理,根据canScrollVertically(int direction)来进行判断。
1 //是否滑到底部
2 if(!recyclerView.canScrollVertically(1)){
3 //相应处理操作
4 }
5 //是否滑到顶部
6 if(!recyclerView.canScrollVertically(-1)){
7 //相应处理操作
8 }
3、自定义RecyclerView
知道了滑动事件的判断和处理,就可以很轻松得实现下拉刷新和加载更多了。
1 import android.content.Context;
2 import android.support.annotation.Nullable;
3 import android.support.v7.widget.RecyclerView;
4 import android.util.AttributeSet;
5 import android.util.Log;
6 import android.view.MotionEvent;
7 import android.view.View;
8
9 /**
10 * Package:com.liuting.library
11 * author:liuting
12 * Date:2017/2/14
13 * Desc:自定义RecycleView,下拉刷新以及上拉加载更多
14 */
15
16 public class RefreshLoadMoreRecycleView extends RecyclerView implements RecyclerView.OnTouchListener{
17 private Boolean isLoadMore;//加载更多标志
18 private Boolean isLoadEnd;//加载到最后的标志
19 private Boolean isLoadStart;//顶部的标志
20 private Boolean isRefresh;//下拉刷新标志
21 private int lastVisibleItem;//最后一项
22 private IOnScrollListener listener;//事件监听
23 private float mLastY;//监听移动的位置
24
25 public RefreshLoadMoreRecycleView(Context context) {
26 super(context);
27 init(context);
28 }
29
30 public RefreshLoadMoreRecycleView(Context context, @Nullable AttributeSet attrs) {
31 super(context, attrs);
32 init(context);
33 }
34
35 public RefreshLoadMoreRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle) {
36 super(context, attrs, defStyle);
37 init(context);
38 }
39
40 /**
41 * 初始化
42 *
43 * @param context
44 */
45 public void init(Context context) {
46 isLoadEnd=false;
47 isLoadStart =true;
48
49 this.addOnScrollListener(new RecyclerView.OnScrollListener() {
50 @Override
51 public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
52 super.onScrollStateChanged(recyclerView, newState);
53 //SCROLL_STATE_DRAGGING 和 SCROLL_STATE_IDLE 两种效果自己看着来
54 if (newState == RecyclerView.SCROLL_STATE_IDLE) {
55 loadData();
56 }
57 }
58
59 @Override
60 public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
61 super.onScrolled(recyclerView, dx, dy);
62 //上滑
63 if(dy>0){
64 //是否滑到底部
65 if(!recyclerView.canScrollVertically(1)){
66 isLoadEnd = true;
67 }else{
68 isLoadEnd = false;
69 }
70 Log.e("TAG","Running--->>isLoadEnd:"+isLoadEnd+dy);
71 }else if(dy<0){
72 //是否滑到顶部
73 if(!recyclerView.canScrollVertically(-1)){
74 isLoadStart=true;
75 }else{
76 isLoadStart=false;
77 }
78
79 }
80 }
81 });
82 this.setOnTouchListener(this);
83 }
84
85 /**
86 *
87 * 加载数据
88 *
89 */
90 private void loadData() {
91 if (isLoadEnd) {
92 // 判断是否已加载所有数据
93 if (isLoadMore) {//未加载完所有数据,加载数据,并且还原isLoadEnd值为false,重新定位列表底部
94 if (getListener() != null) {
95 getListener().onLoadMore();
96 }
97 } else {//加载完了所有的数据
98 if(getListener()!=null){
99 getListener().onLoaded();
100 }
101 }
102 isLoadEnd = false;
103 } else if (isLoadStart) {
104 if(isRefresh){
105 if (getListener() != null) {
106 getListener().onRefresh();
107 }
108 isLoadStart=false;
109 }
110 }
111 }
112
113 @Override
114 public boolean onTouch(View view, MotionEvent motionEvent) {
115 if (mLastY == -1) {
116 mLastY = motionEvent.getRawY();
117 }
118 switch (motionEvent.getAction()){
119 case MotionEvent.ACTION_MOVE:
120 final float deltaY = motionEvent.getRawY() - mLastY;
121 mLastY = motionEvent.getRawY();
122 //向上移动
123 if(deltaY<0){
124 //是否滑到底部
125 if(!this.canScrollVertically(1)){
126 isLoadEnd = true;
127 }else{
128 isLoadEnd = false;
129 }
130 }
131 //向下移动
132 else if(deltaY>0) {
133 //是否滑到顶部
134 if (!this.canScrollVertically(-1)) {
135 isLoadStart = true;
136 } else {
137 isLoadStart = false;
138 }
139 }
140 break;
141 case MotionEvent.ACTION_DOWN:
142 mLastY = motionEvent.getRawY();
143 break;
144 default://重置
145 mLastY = -1;
146 break;
147 }
148
149 return false;
150 }
151
152 //事件监听
153 public interface IOnScrollListener {
154 void onRefresh();
155
156 void onLoadMore();
157
158 void onLoaded();
159 }
160
161 public IOnScrollListener getListener() {
162 return listener;
163 }
164
165 //设置事件监听
166 public void setListener(IOnScrollListener listener) {
167 this.listener = listener;
168 }
169
170 public Boolean getLoadMore() {
171 return isLoadMore;
172 }
173
174 //设置是否支持加载更多
175 public void setLoadMoreEnable(Boolean loadMore) {
176 isLoadMore = loadMore;
177 }
178
179 public Boolean getRefresh() {
180 return isRefresh;
181 }
182
183 //设置是否支持下拉刷新
184 public void setRefreshEnable(Boolean refresh) {
185 isRefresh = refresh;
186 }
187 }
二、实际用例
已经定义好了RecyclerView,下面在Demo中实际使用和处理。
1、定义布局
布局文件很简单,就是一个RecyclerView
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:id="@+id/activity_main"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:orientation="vertical"
9 tools:context="com.liuting.refreshloadmorelistview.MainActivity">
10
11 <com.liuting.library.RefreshLoadMoreRecycleView
12 android:id="@+id/main_recycle_view_data"
13 android:layout_width="match_parent"
14 android:layout_height="match_parent"
15 android:scrollbars="none"
16 />
17 </LinearLayout>
2、定义RecyclerView.Adapter
RecyclerView.Adapter在这里就简单处理了,列表布局直接使用Android自带的。
1 import android.content.Context;
2 import android.support.v7.widget.RecyclerView;
3 import android.view.LayoutInflater;
4 import android.view.View;
5 import android.view.ViewGroup;
6 import android.widget.TextView;
7
8 import java.util.List;
9
10 /**
11 * Package:com.liuting.refreshloadmorelistview.adapter
12 * author:liuting
13 * Date:2017/2/16
14 * Desc:列表Adapter
15 */
16
17 public class RefreshLoadMoreRecycleAdapter extends RecyclerView.Adapter<RefreshLoadMoreRecycleAdapter.ViewHolder> {
18 private List<String> list;
19 private Context context;
20
21 public RefreshLoadMoreRecycleAdapter(Context context,List<String> list) {
22 this.context =context;
23 this.list = list;
24 }
25
26 @Override
27 public RefreshLoadMoreRecycleAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
28 View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_expandable_list_item_1, parent, false);
29 RefreshLoadMoreRecycleAdapter.ViewHolder viewHolder = new RefreshLoadMoreRecycleAdapter.ViewHolder(view);
30 return viewHolder;
31 }
32
33 @Override
34 public void onBindViewHolder(ViewHolder holder, int position) {
35 holder.text.setText(list.get(position));
36 holder.itemView.setTag(position);
37 }
38
39 @Override
40 public int getItemCount() {
41 return list.size();
42 }
43
44 class ViewHolder extends RecyclerView.ViewHolder{
45 private TextView text;
46
47 public ViewHolder(View itemView) {
48 super(itemView);
49 text=(TextView)itemView.findViewById(android.R.id.text1);
50 }
51 }
52 }
3、在Activity中定义好控件以及数据加载操作
实现自定义RecyclerView中的数据加载事件监听,刷新、加载更多以及加载完成。
1 import android.app.ProgressDialog;
2 import android.os.Bundle;
3 import android.os.Handler;
4 import android.os.Message;
5 import android.support.v7.app.AppCompatActivity;
6 import android.support.v7.widget.LinearLayoutManager;
7 import android.widget.Toast;
8
9 import com.liuting.library.RefreshLoadMoreRecycleView;
10 import com.liuting.refreshloadmorelistview.adapter.RefreshLoadMoreRecycleAdapter;
11
12 import java.util.ArrayList;
13 import java.util.List;
14
15 public class MainActivity extends AppCompatActivity implements RefreshLoadMoreRecycleView.IOnScrollListener{
16 private RefreshLoadMoreRecycleView recycleView;//下拉刷新RecycleView
17 private List<String> list;//列表
18 private RefreshLoadMoreRecycleAdapter adapter;//Adapter
19 private ProgressDialog dialog;//提示框
20 private static final int REFRESH_Load=0;//下拉刷新
21 private static final int MORE_Load=1;//加载更多
22 private Handler handler =new Handler(){
23 @Override
24 public void handleMessage(Message msg) {
25 super.handleMessage(msg);
26 switch (msg.what){
27 case REFRESH_Load:
28 recycleView.setLoadMoreEnable(true);
29 dismissDialog();
30 if(list!=null){
31 list.clear();
32 }
33 loadData();
34 adapter.notifyDataSetChanged();
35 break;
36 case MORE_Load:
37 recycleView.setLoadMoreEnable(false);
38 dismissDialog();
39 loadData();
40 adapter.notifyDataSetChanged();
41 break;
42 }
43 }
44 };
45
46 @Override
47 protected void onCreate(Bundle savedInstanceState) {
48 super.onCreate(savedInstanceState);
49 setContentView(R.layout.activity_main);
50 initView();
51 }
52
53 public void initView(){
54 dialog = new ProgressDialog(MainActivity.this);
55
56 list=new ArrayList<>();
57 loadData();
58 recycleView = (RefreshLoadMoreRecycleView)findViewById(R.id.main_recycle_view_data);
59
60 final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this);
61 recycleView.setLayoutManager(linearLayoutManager);
63 adapter = new RefreshLoadMoreRecycleAdapter(MainActivity.this,list);
64 recycleView.setAdapter(adapter);
65 recycleView.setListener(this);
66 recycleView.setRefreshEnable(true);
67 recycleView.setLoadMoreEnable(true);
68 }
69
70 /**
71 * 加载数据
72 */
73 public void loadData(){
74 for(int i=0;i<10;i++ ){
75 list.add("It is "+i);
76 }
77 }
78
79 @Override
80 public void onRefresh() {
81 showDialog();
82 new Thread(){
83 @Override
84 public void run() {
85 super.run();
86 try {
87 sleep(5000);
88 handler.sendEmptyMessage(REFRESH_Load);
89 } catch (InterruptedException e) {
90 e.printStackTrace();
91 }
92 }
93 }.start();
94 }
95
96 @Override
97 public void onLoadMore() {
98 showDialog();
99 new Thread(){
100 @Override
101 public void run() {
102 super.run();
103 try {
104 sleep(5000);
105 handler.sendEmptyMessage(MORE_Load);
106 } catch (InterruptedException e) {
107 e.printStackTrace();
108 }
109 }
110 }.start();
111 }
112
113 @Override
114 public void onLoaded() {
115 Toast.makeText(MainActivity.this,"Loaded all",Toast.LENGTH_SHORT).show();
116 }
117
118 /**
119 * dismiss dialog
120 */
121 private void dismissDialog(){
122 if (dialog!=null&&dialog.isShowing()){
123 dialog.dismiss();
124 }
125 }
126
127 /**
128 * show dialog
129 */
130 private void showDialog(){
131 if (dialog!=null&&!dialog.isShowing()){
132 dialog.show();
133 }
134 }
135 }
三、最终效果图
到这里就轻松实现了RecyclerView的下拉刷新和加载更多了。
源代码放在了Github上:https://github.com/LT5505/RefreshLoadMoreRecycleView