Android 下拉刷新之 SwipeRefreshLayout

一、简介

  • SwipeRefreshLayout 是 Google 官方提供的一个下拉刷新的控件。

  • 注意包的位置:android.support.v4.widget.SwipeRefreshLayout

二、使用

  • 用法很简单,将需要下拉刷新功能的控件放在 SwipeRefreshLayout 中,注意,SwipeRefreshLayout 只能有一个子控件。
2.1 xml 布局文件如下所示:
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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--标题栏-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryDark"
android:fitsSystemWindows="true"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<!--自定义控件-->
<TextView
android:id="@+id/toolbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:gravity="center"
android:text="FloatingActionButton"
android:textSize="20dp"
android:textStyle="bold" />
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<!-- app:layout_behavior="@string/appbar_scrolling_view_behavior" 指定一个布局行为-->
<!--下拉刷新控件 SwipeRefreshLayout-->
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swiperefreshlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<!--RecyclerView-->
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"></android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
<!--FloatingActionButton-->
<android.support.design.widget.FloatingActionButton
android:id="@+id/floating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/floating_icon"
app:fabSize="normal"
app:pressedTranslationZ="10dp"
app:rippleColor="@color/colorAccent"
/>
</android.support.design.widget.CoordinatorLayout>
</RelativeLayout>
  • 可以看到,SwipeRefreshLayout 中包裹了一个 RecyclerView ,也就是说,RecyclerView 是需要下拉刷新的控件。
2.2 代码中还有一些设置
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
public class MyRecyclerViewActivity extends AppCompatActivity {
@BindView(R.id.floating)
FloatingActionButton floating;
@BindView(R.id.toolbar_title)
TextView toolbarTitle;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.recycler_view)
RecyclerView recyclerView;
@BindView(R.id.swiperefreshlayout)
SwipeRefreshLayout swiperefreshlayout;
private Snackbar snackbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview_appbarlayout);
ButterKnife.bind(this);
//设置透明状态栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
}
toolbar.setTitle("");
setSupportActionBar(toolbar);
toolbar.setNavigationIcon(R.drawable.setting);
initRecyclerViewData();
initRcyclerView();
initSwipeRefreshLayout();
}
/**
* 初始化下拉刷新控件,SwipeRefreshLayout
*/
private void initSwipeRefreshLayout() {
//设置刷新进度条的颜色变化,最多可以设置 4 种,加载的颜色是循环播放的。
swiperefreshlayout.setColorSchemeResources(R.color.colorAccent);
//设置手指在屏幕上下拉多少会触发下拉刷新
swiperefreshlayout.setDistanceToTriggerSync(300);
//设置下拉刷新的圆圈背景颜色
swiperefreshlayout.setProgressBackgroundColorSchemeColor(Color.WHITE);
//设置下拉刷新的圆圈大小
swiperefreshlayout.setSize(SwipeRefreshLayout.DEFAULT);
// 设置刷新时候的监听事件
swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//执行刷新之后的操作,一般都是联网请求数据
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
initData();
myRecyclerAdapter.notifyDataSetChanged();
//停止刷新
swiperefreshlayout.setRefreshing(false);
}
private void initData() {
listData.add("我是刷新时候添加的数据");
listData.add("我是刷新时候添加的数据");
listData.add("我是刷新时候添加的数据");
listData.add("我是刷新时候添加的数据");
}
});
}
}).start();
}
});
2.3 运行效果如下:

image

三、添加上拉加载更多

思路: 利用 RecyclerView 的 addOnScrollListener 方法,自己动手实现滑动监听,当屏幕可见的最后一条条目显示出来的时候,实现加载更多的逻辑。

3.1 Activity 中使用时的代码:
1
2
3
4
5
6
7
8
9
//上拉加载更多
recyclerView.addOnScrollListener(new MyRecyclerViewOnScrollListener(linearLayoutManager) {
@Override
public void loadMoreDate() {
listData.add("我是上拉加载时候添加的数据");
listData.add("我是上拉加载时候添加的数据");
myRecyclerAdapter.notifyDataSetChanged();
}
});
3.2 MyRecyclerViewOnScrollListener 中的代码:
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
/**
* RecyclerView 滑动监听,目的:实现上拉加载更过多
*/
public abstract class MyRecyclerViewOnScrollListener extends RecyclerView.OnScrollListener {
private LinearLayoutManager linearLayoutManager;
//屏幕上可见的 item 数量
private int visibleItemCount;
//已经加载出来的 item 数量
private int totalItemCount;
//屏幕上可见的第一个 item
private int firstVisibleItem;
//是否正在上拉加载数据中
private boolean isLoadingMore = false;
//记录之前的数据总数
private int agoneTotle;
public MyRecyclerViewOnScrollListener(LinearLayoutManager linearLayoutManager) {
this.linearLayoutManager = linearLayoutManager;
}
/**
* 滑动状态改变
*
* @param recyclerView 当前滚动的 RecyclerView
* @param newState 当前滚动的状态,有三个值
* public static final int SCROLL_STATE_IDLE = 0;静止没滚动
* public static final int SCROLL_STATE_DRAGGING = 1;用户正在用手指滚动
* public static final int SCROLL_STATE_SETTLING = 2;自动滚动
*/
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
/**
* 正在滑动
*
* @param recyclerView 当前滚动的 RecyclerView
* @param dx 水平滚动距离
* @param dy 垂直滚动距离
* dx > 0 时为手指向左滚动,列表滚动显示右面的内容
* dx < 0 时为手指向右滚动,列表滚动显示左面的内容
* dy > 0 时为手指向上滚动,列表滚动显示下面的内容
* dy < 0 时为手指向下滚动,列表滚动显示上面的内容
*/
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//向下滑动
if (dy > 0) {
visibleItemCount = linearLayoutManager.getChildCount();
totalItemCount = linearLayoutManager.getItemCount();
firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
}
//如果正在加载中
if(isLoadingMore){
//说明加载结束
if(totalItemCount > agoneTotle){
isLoadingMore = false;
agoneTotle = totalItemCount;
}
}
//如果没有正在加载中,并且,当前屏幕上可见 item 的总数 + 屏幕上可见第一条 item 大于等于 目前加载出来的数据总数
if (!isLoadingMore && (visibleItemCount + firstVisibleItem) >= totalItemCount) {
isLoadingMore = true;
//加载更多数据,设置一个抽象方法来实现具体的加载逻辑
loadMoreDate();
}
}
public abstract void loadMoreDate();
}
  • 这种实现方式没有实现加载时的加载过程,用户感知不到!