前言:
SwipeRefreshLayout是Google官方更新的一个Widget,可以实现下拉刷新的效果。该控件继承自ViewGroup在support-v4兼容包下,不过我们需要升级supportlibrary的版本到19.1以上。
之前有使用过SwipeRefreshLayout+RecyclerView来实现过列表的下拉刷新功能。但是考虑到RecyclerView中和ListView相比,竟然没有setEmpty(View view)来用于处理获取数据为空的情况下进行界面数据的展示。这一点我也很郁闷。虽然通过控制界面布局的显示和隐藏或者通过万能适配器的使用也能实现这种效果。但是我还是打算用SwipeRefreshLayout+ListView+setEmptyView来实现简单数据列表的下拉刷新。
当获取数据为空时,用于界面提示的空布局
empty_view_tab.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="33dp"
android:src="@mipmap/empty_view_tab" />
<TextView
android:id="@+id/empty_view_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="暂无内容下拉刷新"
android:textColor="@android:color/darker_gray"
android:textSize="12sp" />
</LinearLayout>
空布局效果如下图:
主界面引用:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_first"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--使用谷歌官方的下拉刷新组件,只有下拉刷新功能-->
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/srl"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/empty_view_tab" />
</FrameLayout>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
为什么SwipeRefreshLayout中要嵌套FrameLayout布局呢?
这是因为SwipeRefreshLayout只能有一个子View,因此我们将ListView和空布局归纳成一个。
如果直接在SwipeRefreshLayout中嵌套不止一个子view的话,那么代码中为ListView设置一下EmptyView,编译运行,当listview无数据的时候,会发现设置无效!!!
主要代码:
private void initView() {
emptyView = (LinearLayout) findViewById(R.id.empty_view);
listView = (ListView) findViewById(R.id.lv);
mAdapter = new StringAdapter();
listView.setAdapter(mAdapter);
listView.setEmptyView(emptyView);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srl);
// 设置下拉进度的背景颜色,默认就是白色的
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(android.R.color.white);
// 设置下拉进度的主题颜色
swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
.....
//todo 进行数据获取
.....
}
});
.......
.......
/**
* 适配器
*/
private class StringAdapter extends BaseAdapter {
@Override
public int getCount() {
return mList.size();
}
@Override
public Object getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = View.inflate(FirstActivity.this, android.R.layout.simple_list_item_1, null);
}
TextView tv = (TextView) convertView;
tv.setGravity(Gravity.CENTER);
tv.setPadding(0, 20, 0, 20);
tv.setText(mList.get(position));
return convertView;
}
}
程序运行之后,你会发现listView列表向上滑动是可以的,但是下拉的时候,出现一个bug,那就是
还未下拉到列表的顶部就触发下拉刷新。(即:只要下拉就会触发下拉刷新)。
参考网上的解决方案:添加listView的滑动监听事件
listView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (listView.getFirstVisiblePosition() == 0 &&
listView.getChildAt(0).getTop() >= 0) {//或者 listView.getChildAt(0).getTop() >= listView.getListPaddingTop())
swipeRefreshLayout.setEnabled(true);
Log.d("TAG", "reach top!!!");
} else {
swipeRefreshLayout.setEnabled(false);
}
}
return false;
}
});
添加完成之后,确实解决了列表拉到头之后才能触发下拉刷新的操作,但是这时候又出来了一个新的bug——–有时候下拉好几遍,才会触发下拉刷新的操作。经常下拉刷新不了。
如下图:
这就尴尬了。从网上搜索资料找到解决方案,可参考优雅的解决SwipeRefreshLayout和ListView的EmptyView共存冲突的问题(全网独创)
只需要更改布局,即可完成下拉刷新的操作。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_first"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/empty_view_tab" />
<!--使用谷歌官方的下拉刷新组件,只有下拉刷新功能-->
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/srl"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
</RelativeLayout>
OK,问题解决。下面向大展示下效果图。
顺便给大家补充点知识点:
通常下拉刷新时,我们的数据来源大都来源服务器。网络不稳定的时候,可能特别耗时。这个时候下拉刷新如何进行超时处理呢?比如以12s作为超时的上限。
可以这样操作,代码如下:
private void setSwipeRefreshLayoutStatus(boolean status) {
Log.i(TAG, "setSwipeRefreshLayoutStatus: status::" + status);
if (status) {
swipeRefreshLayout.setRefreshing(true);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
setSwipeRefreshLayoutStatus(false);
//同时做超时提醒的逻辑
}
}, 12000);
} else {
swipeRefreshLayout.setRefreshing(false);
mHandler.removeCallbacksAndMessages(null);
}
当我们下拉刷新时,在下拉刷新的监听事件事件中进行网络数据的请求操作,同时调用
setSwipeRefreshLayoutStatus(true);
这样的话,如果12s之内拿到数据,则在拿到数据的回调(onSuccess(…..))中调用中
setSwipeRefreshLayoutStatus(false);
来移除我们的计时事件,即:
mHandler.removeCallbacksAndMessages(null);
如果12s内没有拿到数据则
setSwipeRefreshLayoutStatus(false);
//同时做超时提醒的逻辑
....
....
....
伪代码如下::
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
.....
//todo 进行数据获取
syncData();
setSwipeRefreshLayoutStatus(true);
.....
}
});
//发起网络请求,获取数据
private void syncData(){
//在子线程中操作
....
....
void success(){
setSwipeRefreshLayoutStatus(false);
}
void fail(){
......
}
}
/**
**用于超时操作的判断处理逻辑
**/
private void setSwipeRefreshLayoutStatus(boolean status) {
Log.i(TAG, "setSwipeRefreshLayoutStatus: status::" + status);
if (status) {
swipeRefreshLayout.setRefreshing(true);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
setSwipeRefreshLayoutStatus(false);
//同时做超时提醒的逻辑
}
}, 12000);
} else {
swipeRefreshLayout.setRefreshing(false);
mHandler.removeCallbacksAndMessages(null);
}
至此完结,示例很简单,不再附上。
后续会出一篇SwipeRefreshLayout+RecyclerView来实现过列表的下拉刷新以及上划加载更多的文章,敬请关注。如有问题请留言,共同探讨!谢谢