前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(一百二十三)下拉刷新布局SwipeRefreshLayout

Android开发笔记(一百二十三)下拉刷新布局SwipeRefreshLayout

作者头像
aqi00
发布2019-01-18 15:03:55
1.9K0
发布2019-01-18 15:03:55
举报
文章被收录于专栏:老欧说安卓

SwipeRefreshLayout

下拉刷新布局SwipeRefreshLayout是Android又一与时俱进的控件,顾名思义它随着用户手势向下滑动就会触发刷新操作。从实际的下拉效果来看,SwipeRefreshLayout秉承了Android一贯的简洁界面,可定制性并不太好,远不如开源的下拉刷新框架PullToRefresh,但毕竟是原生的控件,用起来比较方便,所以我们还是好好了解了解它。 SwipeRefreshLayout最早在19.1的support-v4库中引入,所以要先确保sdk的“Android Support Library”版本不低于19.1。另外,SwipeRefreshLayout的源码多次升级,因此有新版与旧版之分,两版之间不但支持的方法有区别,而且界面效果也有差异。 下面是SwipeRefreshLayout的常用方法说明: setColorScheme : 设置进度条/圆圈的颜色。(该方法在新版中已被废弃) setOnRefreshListener : 设置刷新监听器。在下拉松开时触发该监听器,需要重写该监听器的onRefresh方法。 setRefreshing : 设置刷新的状态。true表示正在刷新,false表示结束刷新。 isRefreshing : 判断是否正在刷新。 下面是新版增加的方法说明: setColorSchemeColors : 设置进度圆圈的圆环颜色。 setProgressBackgroundColorSchemeColor : 设置进度圆圈的背景颜色。 setProgressViewOffset : 设置进度圆圈的偏移量。第一个参数表示进度圈是否缩放,第二个参数表示进度圈开始出现时距顶端的偏移,第三个参数表示进度圈拉到最大时距顶端的偏移。 setDistanceToTriggerSync : 设置手势向下滑动多少距离才会触发刷新操作。 SwipeRefreshLayout的旧版与新版之间的界面区别主要有: 1、旧版的进度条是布局顶部的一条横线,而新版的布局顶部的一个圆圈。 2、旧版在下拉时,进度条不动,页面会随着向下滑动;而新版在下拉时,页面不再向下滑动,进度圆圈会向下滑动。 这两种显示效果各有千秋,开发者可按照个人喜好决定采用哪种效果。需要注意的是,想要旧版的效果,就得使用旧版的android-support-v4.jar;想要新版的效果,就得使用新版的android-support-v4.jar。新旧两版的v4包见本文末尾的代码工程。 下面是旧版SwipeRefreshLayout的下拉刷新效果截图:

下面是新版SwipeRefreshLayout的下拉刷新效果截图:

下面是简单下拉刷新的示例代码(新旧版通用,只需更换v4包即可):

代码语言:javascript
复制
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.widget.TextView;

public class SimpleActivity extends Activity implements OnRefreshListener {
	
	private TextView tv_simple;
	private SwipeRefreshLayout srl_simple;

	private Handler mHandler = new Handler();

	@SuppressLint("ResourceAsColor")
	@SuppressWarnings("deprecation")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_simple);

		tv_simple = (TextView) findViewById(R.id.tv_simple);
		srl_simple = (SwipeRefreshLayout) findViewById(R.id.srl_simple);
		srl_simple.setOnRefreshListener(this);
		//旧版用下面的setColorScheme设置进度条颜色
		srl_simple.setColorScheme(R.color.red, R.color.orange, R.color.green, R.color.blue);
		//新版用下面的setColorSchemeResources设置进度圆圈颜色
//		srl_simple.setColorSchemeResources(
//				R.color.red, R.color.orange, R.color.green, R.color.blue );
		//旧版v4包中无下面三个方法
//		srl_simple.setProgressBackgroundColorSchemeResource(R.color.black);
//		srl_simple.setProgressViewOffset(true, 0, 50);
//		srl_simple.setDistanceToTriggerSync(100);
	}

	@Override
	public void onRefresh() {
		tv_simple.setText("正在刷新");
		mHandler.postDelayed(mRefresh, 3000);
	}
	
	private Runnable mRefresh = new Runnable() {

		@Override
		public void run() {
			tv_simple.setText("刷新完成");
			srl_simple.setRefreshing(false);
		}
		
	};
	
}

SwipeRefreshLayout+ListView下拉刷新

SwipeRefreshLayout搭配ListView可实现简单的列表数据下拉刷新。 首先要注意:在布局文件中,android.support.v4.widget.SwipeRefreshLayout下面只能有一个直接子视图,如果有多个子视图,那么将只展示第一个子视图,后面的子视图将不予展示。如下面这种写法,只会展示名为tv_listview的TextView数据,而不会展示名为lv_content的ListView数据。

代码语言:javascript
复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp" >

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl_listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        
        <TextView
            android:id="@+id/tv_listview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:paddingTop="10dp"
            android:textColor="#000000"
            android:textSize="17sp"
            android:visibility="gone" />
            
        <ListView
            android:id="@+id/lv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>

下面这个才是正确的写法:

代码语言:javascript
复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp" >

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/srl_listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/tv_listview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:paddingBottom="10dp"
                android:textColor="#000000"
                android:textSize="17sp"
                android:visibility="gone" />

            <ListView
                android:id="@+id/lv_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>

SwipeRefreshLayout自带的刷新提示只有一个进度圆圈,显然不能满足多变的定制化需求,比如我们常常需要展示不同的提示文本和提示图片,这时就要在ListView控件上面增加一块提示区域,用来存放个性化的提示内容。这里我们先在提示区域放置一个TextView控件,用来展示最基本的提示文字,在刷新开始时显示该提示,刷新结束时移除该提示。 下面是SwipeRefreshLayout+ListView的下拉刷新效果截图:

下面是SwipeRefreshLayout+ListView的页面代码示例:

代码语言:javascript
复制
import com.example.exmswipe.adapter.TitleListAdapter;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;

public class ListviewActivity extends Activity implements OnRefreshListener {
	
	private TextView tv_listview;
	private SwipeRefreshLayout srl_listview;

	private Handler mHandler = new Handler();

	private ListView lv_content;
	private String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龙年", "蛇年",
			"马年", "羊年", "猴年", "鸡年", "狗年", "猪年"};
	private void refreshView() {
		TitleListAdapter adapter = new TitleListAdapter(this, yearArray);
		lv_content.setAdapter(adapter);
		lv_content.setOnItemClickListener(adapter);
		lv_content.setOnItemLongClickListener(adapter);
	}

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_listview);

		tv_listview = (TextView) findViewById(R.id.tv_listview);
		srl_listview = (SwipeRefreshLayout) findViewById(R.id.srl_listview);
		srl_listview.setOnRefreshListener(this);
		//旧版用下面的setColorScheme设置进度条颜色
		srl_listview.setColorScheme(R.color.red, R.color.orange, R.color.green, R.color.blue);
		//新版用下面的setColorSchemeResources设置进度圆圈颜色
//		srl_listview.setColorSchemeResources(
//				R.color.red, R.color.orange, R.color.green, R.color.blue );
		
		lv_content = (ListView) findViewById(R.id.lv_content);
		refreshView();
	}

	@Override
	public void onRefresh() {
		tv_listview.setVisibility(View.VISIBLE);
		tv_listview.setText("正在刷新");
		mHandler.postDelayed(mRefresh, 3000);
	}
	
	private Runnable mRefresh = new Runnable() {

		@Override
		public void run() {
			refreshView();
			tv_listview.setText("刷新完成");
			srl_listview.setRefreshing(false);
			mHandler.postDelayed(mComplete, 500);
		}
		
	};

	private Runnable mComplete = new Runnable() {

		@Override
		public void run() {
			tv_listview.setVisibility(View.GONE);
		}
		
	};

}

SwipeRefreshLayout+RecyclerView下拉刷新

看过了搭配ListView的下拉刷新,再来看看搭配RecyclerView会是什么效果。上面我们看到搭配ListView的情况下,在刷新开始与刷新结束时,提示文字的展示与隐藏过程有点突兀,都是一下子展示和一下子隐藏,缺乏动画效果,使人觉得生硬呆板。那么搭配RecyclerView进行下拉刷新有没有办法改善这种情况呢? 下面是SwipeRefreshLayout+RecyclerView的下拉刷新效果截图:

从上面截图可以发现,提示文字的出现和消失都伴随着动画,整体效果显得柔和许多,而这有赖于RecyclerView的局部更新特性。复习一下前篇博文《Android开发笔记(一百二十二)循环器视图RecyclerView》,当时我们提到RecyclerView的几个方法,却没有实际运用的例子,现在正好派上用场了。它们是: 1、RecyclerView.Adapter的notify相关方法,如notifyItemInserted、notifyItemRemoved、notifyItemChanged,用于在个别项目发生变化时单独通知适配器调整。 2、GridLayoutManager的setSpanSizeLookup方法,对于第一个元素,可指定让它占满第一行作为头部区域,从而把提示区域纳入整个列表视图的统一管理。 3、GridLayoutManager的setLayoutManager方法,在布局管理信息发生变化时,随时调用该方法生效最新的布局配置,这样列表项的增删操作就能显示动画效果。 下面是SwipeRefreshLayout+RecyclerView的页面代码示例:

代码语言:javascript
复制
import java.util.LinkedList;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;

import com.example.exmswipe.adapter.RecyclerAdapter;

public class RecyclerActivity extends Activity implements OnRefreshListener {
	
	private SwipeRefreshLayout srl_recycler;

	private Handler mHandler = new Handler();

	private int mColumnCount = 3;
	private RecyclerView rv_content;
	private GridLayoutManager mLayoutManager;
	private RecyclerAdapter mAdapter;
	private String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龙年", "蛇年",
			"马年", "羊年", "猴年", "鸡年", "狗年", "猪年"};
	private LinkedList<String> mYearList = new LinkedList<String>();
	private void refreshView() {
		mAdapter = new RecyclerAdapter(this, 2, mYearList);
		rv_content.setAdapter(mAdapter);
		mAdapter.setOnItemClickListener(mAdapter);
		mAdapter.setOnItemLongClickListener(mAdapter);
	}

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_recycler);

		srl_recycler = (SwipeRefreshLayout) findViewById(R.id.srl_recycler);
		srl_recycler.setOnRefreshListener(this);
		//旧版用下面的setColorScheme设置进度条颜色
		srl_recycler.setColorScheme(R.color.red, R.color.orange, R.color.green, R.color.blue);
		//新版用下面的setColorSchemeResources设置进度圆圈颜色
//		srl_recycler.setColorSchemeResources(
//				R.color.red, R.color.orange, R.color.green, R.color.blue );
		
		rv_content = (RecyclerView) findViewById(R.id.rv_content);
		mLayoutManager = new GridLayoutManager(this, mColumnCount);
		mLayoutManager.setSpanSizeLookup(new MySpanRule(-1));
		rv_content.setLayoutManager(mLayoutManager);
		for (int i=0; i<yearArray.length; i++) {
			mYearList.add(yearArray[i]);
		}
		refreshView();
	}

	@Override
	public void onRefresh() {
		mYearList.addFirst("正在刷新");
		mAdapter.notifyItemInserted(0);
		mLayoutManager.setSpanSizeLookup(new MySpanRule(0));
		rv_content.setLayoutManager(mLayoutManager);
		
		mHandler.postDelayed(mRefresh, 3000);
	}
	
	private Runnable mRefresh = new Runnable() {

		@Override
		public void run() {
			mYearList.set(0, "刷新完成");
			mAdapter.notifyItemChanged(0);
			srl_recycler.setRefreshing(false);
			mHandler.postDelayed(mComplete, 500);
		}
		
	};

	private Runnable mComplete = new Runnable() {

		@Override
		public void run() {
			mYearList.removeFirst();
			mAdapter.notifyItemRemoved(0);
			mLayoutManager.setSpanSizeLookup(new MySpanRule(-1));
			rv_content.setLayoutManager(mLayoutManager);
		}
		
	};
	
	private class MySpanRule extends GridLayoutManager.SpanSizeLookup {
		
		private int mPosition;
		public MySpanRule(int position) {
			mPosition = position;
		}

		@Override
		public int getSpanSize(int position) {
			if (mPosition == -1) {
				return 1;
			} else if (position == mPosition) {
				return mColumnCount;
			} else {
				return 1;
			}
		}
		
	}

}

SwipeRefreshLayout实现上拉加载

SwipeRefreshLayout默认只实现下拉刷新功能,没有上拉加载功能;如果我们要用它来做上拉加载,就得想办法自定义控件了。从网上资料来看,大家给SwipeRefreshLayout添加上拉加载主要有两种思路: 1、重写SwipeRefreshLayout,在dispatchTouchEvent方法中捕获上拉事件,并进行合理性校验之后,触发上拉加载操作。 2、调用RecyclerView的setOnTouchListener方法,并实现一个触摸监听器传给该方法,监听器中也是一样捕获上拉事件并进行后续处理。但是该方法不能与SwipeRefreshLayout直接共存,因为SwipeRefreshLayout与ScrollView一样,都会自动拦截上下滑动的手势,如此一来,上下滑动事件都被SwipeRefreshLayout先拦截了,哪里还会传给下面的RecyclerView呢?所以要么只用RecyclerView不用SwipeRefreshLayout,要么就是重写SwipeRefreshLayout让它放过上拉事件。 上面两种思路都有对应的实现代码,可是我觉得,SwipeRefreshLayout作为Android的原生控件,就应该让它去做它擅长的事情。如果要它实现上拉加载还得大动干戈的话,不如直接使用下拉刷新的开源框架PullToRefresh,因为PullToRefresh能够实现的功能更加强大,如果都要大费周章,为什么我们不用更好的呢?所以我以为上拉加载功能最好还是用PullToRefresh,有关PullToRefresh的详细介绍参见《Android开发笔记(十二)测量尺寸与下拉刷新》。 点击下载本文用到的下拉刷新的工程代码 点此查看Android开发笔记的完整目录

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年09月02日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SwipeRefreshLayout
  • SwipeRefreshLayout+ListView下拉刷新
  • SwipeRefreshLayout+RecyclerView下拉刷新
  • SwipeRefreshLayout实现上拉加载
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档