下拉刷新是一种比较常用的效果,Android 5.0之前官方并未提供类似的控件,App中主要是用的第三方库,例如PullToRefresh,ActionBar-PullToRefresh等。刚好现在项目中需要处理 Android 5.0 材质设计部分的东西,就顺带学习下这部分。
大体介绍一下;
- SwipeRefreshLayout是Google在support v4 19.1版本的library更新的一个下拉刷新控件 (android-support-v4.jar)
- 目前只支持下拉刷新,不支持上拉加载更多的操作(需要自行进行扩展)
- 作为官方自带的控件,相对能能够保证比较好的通用性及风格(这里不包括各种自家定制的系统 L)
- SwipeRefreshLayout继承于ViewGroup,ViewGroup中可以包含其他不同控件,so UI定制起来也相对比较容易
- 使用起来比较方便,可以很容易的实现Google Now的刷新效果
SwipeRefreshLayout布局中目前只能包含一个子布局,使用侦听机制来通知刷新事件。例如当用户使用下拉手势时,SwipeRefreshLayout会触发OnRefreshListener,然后刷新事件会在onRefresh()方法中进行处理。当需要结束刷新的时候,可以调用setRefreshing(false)。如果要禁用手势和进度动画,调用setEnabled(false)即可。
接下来介绍一下其大体使用方法:
1.布局文件(示例代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<?xml version=
"1.0"
encoding=
"utf-8"
?>
<FrameLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<android.support.v4.widget.SwipeRefreshLayout
android:id=
"@+id/id_explore_swipe_ly"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:background=
"#ffffff"
>
<ListView
android:id=
"@+id/id_listview"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
</ListView>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
|
2.java逻辑代码:
首先需要实现 SwipeRefreshLayout.OnRefreshListener 接口,然后重写方法 onRefresh():
1
2
3
4
5
6
7
8
9
10
11
|
@Override
public
void
onRefresh() {
new
Handler().postDelayed(
new
Runnable() {
@Override
public
void
run() {
// 设置SwipeRefreshLayout当前是否处于刷新状态,一般是在请求数据的时候设置为true,在数据被加载到View中后,设置为false。
mSwipeRefreshLayout.setRefreshing(
false
);
}
},
3000
);
}
|
现在我们初始化该控件:
1
2
3
4
5
6
7
8
9
10
|
public
void
initSwipeRefreshParameters() {
// 设置进度条的颜色变化,最多可以设置4种颜色
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_green_dark, android.R.color.holo_green_light,
android.R.color.holo_orange_light, android.R.color.holo_red_light);
// 设置下拉监听,当用户下拉的时候会去执行回调
mSwipeRefreshLayout.setOnRefreshListener(
this
);
// 调整进度条距离屏幕顶部的距离
mSwipeRefreshLayout.setProgressViewOffset(
false
,
0
,
(
int
) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
24
, getResources().getDisplayMetrics()));
}
|
以上是基本用法,现在来大体介绍一下定制支持上拉加载的部分,演示图示:
图1 上拉加载效果示意图
首先实现SwipeRefreshLayout的重写:
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
public
class
mySwipeRefreshLayout
extends
SwipeRefreshLayout
implements
OnScrollListener {
private
int
mTouchSlop;
private
ListView mListView;
private
OnLoadListener mOnLoadListener;
private
View mListViewFooter;
private
int
mYDown;
private
int
mLastY;
private
boolean
isLoading =
false
;
public
mySwipeRefreshLayout(Context context) {
this
(context,
null
);
}
public
mySwipeRefreshLayout(Context context, AttributeSet attrs) {
super
(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mListViewFooter = LayoutInflater.from(context).inflate(R.layout.listview_footer,
null
,
false
);
}
@Override
protected
void
onLayout(
boolean
changed,
int
left,
int
top,
int
right,
int
bottom) {
super
.onLayout(changed, left, top, right, bottom);
// 初始化ListView对象
if
(mListView ==
null
) {
getListView();
}
}
private
void
getListView() {
int
childs = getChildCount();
if
(childs >
0
) {
View childView = getChildAt(
0
);
if
(childView
instanceof
ListView) {
mListView = (ListView) childView;
// 设置滚动监听器给ListView, 使得滚动的情况下也可以自动加载
mListView.setOnScrollListener(
this
);
}
}
}
public
void
setListView(ListView list) {
this
.mListView = list;
setLoading(
true
);
this
.mListView.setOnScrollListener(
this
);
}
@Override
public
boolean
dispatchTouchEvent(MotionEvent event) {
final
int
action = event.getAction();
switch
(action) {
case
MotionEvent.ACTION_DOWN:
mYDown = (
int
) event.getRawY();
break
;
case
MotionEvent.ACTION_MOVE:
mLastY = (
int
) event.getRawY();
break
;
case
MotionEvent.ACTION_UP:
if
(canLoad()) {
loadData();
}
break
;
default
:
break
;
}
return
super
.dispatchTouchEvent(event);
}
/**
* @方法说明:是否可以加载更多, 条件是到了最底部, listview不在加载中, 且为上拉操作.
*/
private
boolean
canLoad() {
return
isBottom() && !isLoading && isPullUp();
}
/**
* @方法说明:判断是否到了最底部
*/
private
boolean
isBottom() {
if
(mListView !=
null
&& mListView.getAdapter() !=
null
) {
return
mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() -
1
);
}
return
false
;
}
/**
* @方法说明:是否是上拉操作
*/
private
boolean
isPullUp() {
return
(mYDown - mLastY) >= mTouchSlop;
}
/**
* @方法说明: 如果到了最底部,而且是上拉操作.那么执行onLoad方法
*/
private
void
loadData() {
if
(mOnLoadListener !=
null
) {
mOnLoadListener.onLoad();
}
// 设置状态
setLoading(
true
);
}
/**
* @方法说明:设置刷新
*/
public
void
setLoading(
boolean
loading) {
isLoading = loading;
if
(mListView !=
null
&& mListView.getFooterViewsCount() >
0
)
mListView.removeFooterView(mListViewFooter);
if
(isLoading) {
if
(mListView !=
null
&& mListView.getFooterViewsCount() <=
0
)
mListView.addFooterView(mListViewFooter);
}
else
{
mYDown =
0
;
mLastY =
0
;
}
}
public
void
setOnLoadListener(OnLoadListener loadListener) {
mOnLoadListener = loadListener;
}
@Override
public
void
onScrollStateChanged(AbsListView view,
int
scrollState) {
}
@Override
public
void
onScroll(AbsListView view,
int
firstVisibleItem,
int
visibleItemCount,
int
totalItemCount) {
if
(isFastDoubleClick(
100
))
return
;
// 滚动时到了最底部也可以加载更多
isLoading =
false
;
if
(canLoad()) {
loadData();
}
}
public
View getmListViewFooter() {
return
mListViewFooter;
}
/**
* @类描述:加载更多的监听器
*/
public
static
interface
OnLoadListener {
public
void
onLoad();
}
private
static
long
lastClickTime;
public
static
boolean
isFastDoubleClick(
long
times) {
long
time = System.currentTimeMillis();
long
timeD = time - lastClickTime;
if
(
0
< timeD && timeD < times) {
return
true
;
}
lastClickTime = time;
return
false
;
}
}
|
MainActivity.java
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
|
public
class
MainActivity
extends
Activity {
private
mydapter adapter;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
adapter =
new
mydapter();
// 获取RefreshLayout实例
final
mySwipeRefreshLayout myRefreshListView = (mySwipeRefreshLayout) findViewById(R.id.swipe_layout);
// 获取listview实例
ListView listView = (ListView) findViewById(R.id.listview);
myRefreshListView.setListView(listView);
listView.setAdapter(adapter);
// 设置进度条的颜色变化,最多可以设置4种颜色
myRefreshListView.setColorSchemeResources(android.R.color.holo_green_dark, android.R.color.holo_green_light,
android.R.color.holo_orange_light, android.R.color.holo_red_light);
// 设置下拉刷新监听器
myRefreshListView.setOnRefreshListener(
new
OnRefreshListener() {
@Override
public
void
onRefresh() {
Toast.makeText(MainActivity.
this
,
"refresh"
, Toast.LENGTH_SHORT).show();
myRefreshListView.postDelayed(
new
Runnable() {
@Override
public
void
run() {
// 更新完后调用该方法结束刷新
myRefreshListView.setRefreshing(
false
);
adapter.getData().clear();
// 模拟一些数据
for
(
int
i =
0
; i <
20
; i++) {
adapter.addData(
"liu hhh "
+ i);
}
}
},
1000
);
}
});
// 加载监听器
myRefreshListView.setOnLoadListener(
new
OnLoadListener() {
@Override
public
void
onLoad() {
myRefreshListView.postDelayed(
new
Runnable() {
@Override
public
void
run() {
// 加载完后调用该方法
adapter.addData(
new
Date().toGMTString());
adapter.notifyDataSetChanged();
myRefreshListView.setLoading(
false
);
}
},
1500
);
}
});
}
class
mydapter
extends
BaseAdapter {
List<String> datas =
new
ArrayList<String>();
public
mydapter() {
// 模拟一些数据
for
(
int
i =
0
; i <
20
; i++) {
datas.add(
"item - "
+ i);
}
}
public
void
setData(List<String> data) {
this
.datas = data;
notifyDataSetChanged();
}
public
void
addData(String str) {
this
.datas.add(str);
notifyDataSetChanged();
}
public
List<String> getData() {
return
datas;
}
@Override
public
int
getCount() {
return
datas.size();
}
@Override
public
Object getItem(
int
position) {
return
datas.get(position);
}
@Override
public
long
getItemId(
int
position) {
return
position;
}
@Override
public
View getView(
int
position, View convertView, ViewGroup parent) {
if
(convertView ==
null
) {
convertView = MainActivity.
this
.getLayoutInflater().inflate(R.layout.item,
null
);
}
TextView tv = (TextView) convertView.findViewById(R.id.text);
tv.setText(datas.get(position));
return
convertView;
}
}
}
|
listView_footer:
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
|
<?
xml
version="1.0" encoding="utf-8"?>
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/very_light_gray"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:paddingBottom="10dip"
android:paddingTop="10dip" >
<
ProgressBar
android:id="@+id/pull_to_refresh_load_progress"
style="@android:style/Widget.ProgressBar.Small.Inverse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:indeterminate="true"
android:paddingRight="10dp" />
<
TextView
android:id="@+id/pull_to_refresh_loadmore_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_toRightOf="@+id/pull_to_refresh_load_progress"
android:paddingTop="5dip"
android:text="@string/loading"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@android:color/darker_gray"
android:textSize="14sp"
android:textStyle="bold" />
</
RelativeLayout
>
|
main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?
xml
version="1.0" encoding="utf-8"?>
android:id="@+id/swipe_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<
ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</
ListView
>
</
com.example.demo.mySwipeRefreshLayout
>
|
item.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?
xml
version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<
TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:padding="10dp"
android:text="wo lai le"
android:background="@color/very_light_gray"/>
<
View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/white"/>
</
LinearLayout
>
|
参考链接
http://developer.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html