下拉刷新是一种比较常用的效果,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.布局文件(示例代码)
<?xml version=
"1.0"
encoding=
"utf-8"
?>
<FrameLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
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():
@Override
public
void
onRefresh() {
new
Handler().postDelayed(
new
Runnable() {
@Override
public
void
run() {
// 设置SwipeRefreshLayout当前是否处于刷新状态,一般是在请求数据的时候设置为true,在数据被加载到View中后,设置为false。
mSwipeRefreshLayout.setRefreshing(
false
);
}
},
3000
);
}
现在我们初始化该控件:
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()));
}
以上是基本用法,现在来大体介绍一下定制支持上拉加载的部分,演示图示:
首先实现SwipeRefreshLayout的重写:
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
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:
<?
xml
version="1.0" encoding="utf-8"?>
<
RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
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
<?
xml
version="1.0" encoding="utf-8"?>
<
com.example.demo.mySwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
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:
<?
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="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
>