Android下拉刷新效果实现

时间:2022-09-18 23:41:09

本文主要包括以下内容

  1. 自定义实现pulltorefreshView
  2. 使用google官方SwipeRefreshLayout

下拉刷新大致原理

判断当前是否在最上面而且是向下滑的,如果是的话,则加载数据,并更新界面。

自定义实现pulltorefreshView

package com.jimstin.pulltorefreshviewdemo.view;

import com.jimstin.pulltorefreshviewdemo.R;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation; public class PullToRefreshView extends LinearLayout implements OnTouchListener { private static PullToRefreshView mPullToRefreshView;
private static Context mContext;
private AttributeSet mAttrs;
private View[] childs;
private RelativeLayout mTopView, mBottomView;
private MyScrollView mScrollView;
private LinearLayout mContentView;
private LayoutParams mParams;
private static TextView mTopTips, mBottomTips;
private static ImageView mTopTipsIcon, mBottomTipsIcon;
private RotateAnimation toAnmt, backAnmt, progressBarAnmt; private boolean isToTop, isToBottom;
private boolean isGetChilds;
private boolean isRecordY;
private boolean isContentNotFull;
private boolean isResetLayoutParams;
private boolean isActviteScrollEvent;//偏移量达到刷新或加载更多的要求时,为true,否则为false
private boolean isTurnUp;//判断手势,向上时为true,向下为false
private static boolean isRefresing;//是否正在刷新
private static boolean isRefreshed = true;//是否已刷新完毕
private static boolean isLoading;//是否正在加载
private static boolean isLoaded = true;//是否已加载完毕
private static boolean isPulling;//是否正在拖动scrollview
private boolean isRotate;//是否已经动画 private static int topViewHeight;//topview的高度,和bottomview高度一致
private int mHeight;//整个PullToRefreshView的高度
private int scrollToEnd;//scrollview滚动到底部的长度
private float lastY;//
private float guestureLastY;//用于记录手势按下屏幕时的第一个位置 private OnRefreshListener mRefreshListener;
private OnLoadListener mLoadListener; public PullToRefreshView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
this.mAttrs = attrs;
initView();
} private void initView() {
mPullToRefreshView = this;
toAnmt = new RotateAnimation(0, 180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
toAnmt.setDuration(300);
toAnmt.setFillAfter(true);
backAnmt = new RotateAnimation(0, -180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
backAnmt.setDuration(300);
backAnmt.setFillAfter(true);
progressBarAnmt = new RotateAnimation(0, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
progressBarAnmt.setDuration(1800);
progressBarAnmt.setRepeatCount(-1);
progressBarAnmt.setRepeatMode(RotateAnimation.RESTART);
progressBarAnmt.setInterpolator(new LinearInterpolator());
toAnmt.setAnimationListener(new AnimationListener() { @Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub } @Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub } @Override
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
mTopTipsIcon.clearAnimation();
mTopTipsIcon.setBackgroundResource(R.drawable.ic_up);
mBottomTipsIcon.clearAnimation();
mBottomTipsIcon.setBackgroundResource(R.drawable.ic_up);
} }); backAnmt.setAnimationListener(new AnimationListener() { @Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub } @Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub } @Override
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
mTopTipsIcon.clearAnimation();
mTopTipsIcon.setBackgroundResource(R.drawable.ic_down);
mBottomTipsIcon.clearAnimation();
mBottomTipsIcon.setBackgroundResource(R.drawable.ic_down);
}
}); ViewTreeObserver ob = getViewTreeObserver();
ob.addOnPreDrawListener(new OnPreDrawListener() { @Override
public boolean onPreDraw() {
if(topViewHeight == 0 || mHeight == 0) {
if(topViewHeight == 0) {
topViewHeight = mTopView.getHeight();
}
if(mHeight == 0) {
mHeight = getHeight();
}
if(mHeight > 0 && topViewHeight > 0) {
SharedPreferences p = mContext.getSharedPreferences("PullToRefreshView", 0);
p.edit().putInt("barHeight", topViewHeight).putInt("height", mHeight).commit();
}
mParams = new LayoutParams(LayoutParams.MATCH_PARENT, mHeight+2*topViewHeight);
setLayoutParams(mParams);
setY(-topViewHeight);
}
return true;
}
});
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} @Override
protected void onFinishInflate() {
// TODO Auto-generated method stub
super.onFinishInflate();
setLayout();
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
} public void setLayout() {
if(!isGetChilds) { int count = getChildCount();
childs = new View[count];
for(int i=0; i<count; i++) {
childs[i] = getChildAt(i);
}
for(int i=0; i<count; i++) {
removeAllViews();
}
LayoutInflater inflater = LayoutInflater.from(mContext);
mTopView = (RelativeLayout) inflater.inflate(R.layout.layout_top_pull_to_refresh_view, null);
mBottomView = (RelativeLayout) inflater.inflate(R.layout.layout_bottom_pull_to_refresh_view, null);
mTopTips = (TextView) mTopView.findViewById(R.id.top_tips);
mBottomTips = (TextView) mBottomView.findViewById(R.id.bottom_tips);
mTopTipsIcon = (ImageView) mTopView.findViewById(R.id.ic_down);
mBottomTipsIcon = (ImageView) mBottomView.findViewById(R.id.ic_up); mScrollView = new MyScrollView(mContext, mAttrs);
mParams = new LayoutParams(LayoutParams.MATCH_PARENT, 0);
mParams.weight = 1;
mScrollView.setLayoutParams(mParams);
mContentView = new LinearLayout(mContext, mAttrs);
mContentView.setLayoutParams(mParams);
mContentView.setOrientation(LinearLayout.VERTICAL);
for(int i=0; i<count; i++) {
mContentView.addView(childs[i]);
}
mScrollView.addView(mContentView);
mScrollView.setOnTouchListener(this);
addView(mTopView);
addView(mScrollView);
addView(mBottomView);
isGetChilds = true;
}
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mScrollView.getHeight() > mContentView.getHeight()) {
isContentNotFull = true;
mBottomView.setVisibility(View.GONE);
} else {
isContentNotFull = false;
}
}
/**
* 设置刷新监听器
*/
public void setOnRefreshListener(OnRefreshListener listener) {
mRefreshListener = listener;
}
/**
* 设置加载监听器
*/
public void setOnLoadListener(OnLoadListener listener) {
mLoadListener = listener;
} class MyScrollView extends ScrollView { public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
} @Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
//MyLog.i("t="+t);
if(t == 0) {
isToTop = true;
} else {
if(t == getChildAt(0).getHeight()-getHeight()) {
scrollToEnd = t;
isToBottom = true;
}
}
}
} /**
* 刷新监听器
*/
public static abstract class OnRefreshListener {
/**
* 正在刷新
*/
public void onRefresh() {
}
/**
* 停止刷新,刷新完毕时请主动调用
*/
public void stopRefresh() {
isRefresing = false;
mTopTips.setText(mContext.getResources().getString(R.string.refreshed));
mTopTipsIcon.clearAnimation();
mTopTipsIcon.setBackgroundResource(R.drawable.ic_finish);
resetYOffset();
}
}
/**
* 加载更多监听器
*
*/
public static abstract class OnLoadListener {
/**
* 正在加载
*/
public void onLoad() {
}
/**
* 停止加载,加载完毕时请主动调用
*/
public void stopLoad() {
isLoading = false;
mBottomTips.setText(mContext.getResources().getString(R.string.loaded));
mBottomTipsIcon.clearAnimation();
mBottomTipsIcon.setBackgroundResource(R.drawable.ic_finish);
resetYOffset();
}
} public static void resetYOffset() {
new AsyncTask<Integer, Integer, Integer>() { @Override
protected Integer doInBackground(Integer... params) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
return null;
} protected void onPostExecute(Integer result) {
if(!isPulling) {
mPullToRefreshView.setY(-topViewHeight);
mTopTipsIcon.setBackgroundResource(R.drawable.ic_down);
mBottomTipsIcon.setBackgroundResource(R.drawable.ic_up);
isRefreshed = true;
isLoaded = true;
} };
}.execute();
} @Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
guestureLastY = event.getRawY();
isPulling = true;
break;
case MotionEvent.ACTION_MOVE: if(event.getRawY() - guestureLastY > 0) {
isTurnUp = false;
} else {
isTurnUp = true;
}
if(isContentNotFull && !isResetLayoutParams) {
mParams = new LayoutParams(LayoutParams.MATCH_PARENT, getHeight()-topViewHeight);
setLayoutParams(mParams);
isResetLayoutParams = true;
}
if(isToTop && !isTurnUp) { if(!isRecordY) {
lastY = event.getRawY()/3;
isRecordY = true;
} mScrollView.setVerticalScrollBarEnabled(false);
float y = 0.0f;
if(!isRefreshed) {
if(!isPulling) {
y = event.getRawY()/3 - lastY - topViewHeight;
} else {
y = event.getRawY()/3 - lastY;
}
} else {
y = event.getRawY()/3 - lastY - topViewHeight;
}
if(!isRefresing && isRefreshed) {
if(Math.abs(event.getRawY()/3 - lastY) >= topViewHeight) {
isActviteScrollEvent = true;
mTopTips.setText(mContext.getResources().getString(R.string.release_to_refresh));
if(!isRotate) {
mTopTipsIcon.startAnimation(toAnmt);
isRotate = true;
} } else { isActviteScrollEvent = false;
mTopTips.setText(mContext.getResources().getString(R.string.pull_to_refresh));
if(isRotate) {
mTopTipsIcon.startAnimation(backAnmt);
isRotate = false;
}
}
}
setY(y);
if(event.getRawY()/3 - lastY < 0) {
isToTop = false;
isRecordY = false;
} else {
mScrollView.setScrollY(0);
}
} else if(isToBottom || isContentNotFull) { if(!isRecordY) {
lastY = event.getRawY()/3;
isRecordY = true;
} mScrollView.setVerticalScrollBarEnabled(false); if(!isLoading && isLoaded) {
if(Math.abs(event.getRawY()/3 - lastY) >= topViewHeight) {
isActviteScrollEvent = true;
mBottomTips.setText(mContext.getResources().getString(R.string.release_to_load));
if(!isRotate) {
mBottomTipsIcon.startAnimation(backAnmt);
isRotate = true;
}
} else {
isActviteScrollEvent = false;
mBottomTips.setText(mContext.getResources().getString(R.string.push_to_load));
if(isRotate) {
mBottomTipsIcon.startAnimation(toAnmt);
isRotate = false;
}
}
}
float y = 0.0f;
if(!isLoaded) {
if(isPulling) {
y = event.getRawY()/3 - lastY - 2*topViewHeight;
} else {
y = event.getRawY()/3 - lastY - topViewHeight;
}
} else {
y = event.getRawY()/3 - lastY - topViewHeight;
}
setY(y);
if(event.getRawY()/3 - lastY > 0) {
isToBottom = false;
isRecordY = false;
} else {
mScrollView.setScrollY(scrollToEnd);
}
}
break;
case MotionEvent.ACTION_UP: mScrollView.setVerticalScrollBarEnabled(true);
isRotate = false;
isRecordY = false;
isPulling = false;
if(isActviteScrollEvent) {
if(isToTop && !isTurnUp) {
if(mRefreshListener != null) {
isRefresing = true;
isRefreshed = false;
setY(0);
mTopTips.setText(mContext.getResources().getString(R.string.refreshing));
mTopTipsIcon.setBackgroundResource(R.drawable.ic_refresh_progress_bar);
mTopTipsIcon.startAnimation(progressBarAnmt);
mRefreshListener.onRefresh();
}
} else if(isToBottom) {
if(mLoadListener != null) {
setY(-2*topViewHeight);
mBottomTips.setText(mContext.getResources().getString(R.string.loading));
mBottomTipsIcon.setBackgroundResource(R.drawable.ic_refresh_progress_bar);
mBottomTipsIcon.startAnimation(progressBarAnmt);
isLoading = true;
isLoaded = false;
mLoadListener.onLoad();
}
} else if(isContentNotFull) {
setY(-topViewHeight);
} isActviteScrollEvent = false;
} else {
if(isRefresing) {
setY(0);
} else if(isLoading) {
setY(-2*topViewHeight);
} else {
setY(-topViewHeight); }
}
if(!isRefresing) {
isRefreshed = true;
}
if(!isLoading) {
isLoaded = true;
} isToTop = false;
isToBottom = false;
break; default:
break; }
return false;
} public void initLayoutParams() {
SharedPreferences p = mContext.getSharedPreferences("PullToRefreshView", 0);
topViewHeight = p.getInt("barHeight", 0);
mHeight = p.getInt("height", 0); if(mHeight > 0) {
mParams = new LayoutParams(LayoutParams.MATCH_PARENT, mHeight+2*topViewHeight);
setLayoutParams(mParams);
setY(-topViewHeight);
}
}
}

参考链接

PullToRefreshView下拉刷新上来加载更多,支持任何子view! - zhangjm_123的专栏 - 博客频道 - CSDN.NET

SwipeRefreshLayout 使用

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:padding="8dp"
android:background="#Ffffff"
>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout_caselist"
android:layout_marginTop="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"> <ListView
android:id="@+id/list"
android:scrollbars="none"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="0dp"
android:divider="#Ffffff"> </ListView>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>

在Activity中使用

 @Override
protected void setUpView() {
if (mIsFirstCreated)
{
onRefresh();
} listView = $(R.id.list);
swipeRefreshLayout=$(R.id.swipe_refresh_layout_caselist);
//设置刷新时动画的颜色,可以设置4个
swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light); swipeRefreshLayout.setOnRefreshListener(this);
getData();
// BaseAdapter arrayAdapter = new ArrayAdapter(getActivity(),R.layout.item_list,R.id.name,
// Arrays.asList("美食劵", "活动劵", "优惠劵", "团购劵", "外卖劵"));
// listView.setAdapter(arrayAdapter); } @Override
public void onRefresh() {
//tv.setText("正在刷新");
// TODO Auto-generated method stub
mIsFirstCreated = false;
getData(); }

使用的类要实现SwipeRefreshLayout.OnRefreshListener接口

并且在数据获取成功后发送消息,设置swipeRefreshLayout.setRefreshing(false);

参考链接

Google自己的下拉刷新组件SwipeRefreshLayout - 李金尧 - 博客园

你还在用开源控件的下拉刷新吗?你out了,试一试官方的下拉刷新SwipeRefreshLayout - 推酷

Android下拉刷新效果实现的更多相关文章

  1. Android下拉刷新-SwipeRefreshLayout&comma;RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout&rpar;

    SwipeRefrshLayout是Google官方更新的一个Widget,可以实现下拉刷新的效果.该控件集成自ViewGroup在support-v4兼容包下,不过我们需要升级supportlibr ...

  2. 【原创】窥视懒人的秘密---android下拉刷新开启手势的新纪元

    小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处 **************************************************** ...

  3. Android 下拉刷新上拉载入 多种应用场景 超级大放送(上)

    转载请标明原文地址:http://blog.csdn.net/yalinfendou/article/details/47707017 关于Android下拉刷新上拉载入,网上的Demo太多太多了,这 ...

  4. 移动端上拉加载,下拉刷新效果Demo

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. &lbrack;Swift通天遁地&rsqb;二、表格表单-&lpar;4&rpar;使用系统自带的下拉刷新控件,制作表格的下拉刷新效果

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  6. &lbrack;Swift通天遁地&rsqb;二、表格表单-&lpar;6&rpar;创建美观的表格弹性下拉刷新效果

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  7. Android 下拉刷新上拉载入效果功能

    应用场景: 在App开发中,对于信息的获取与演示.不可能所有将其获取与演示,为了在用户使用中,给予用户以友好.方便的用户体验,以滑动.下拉的效果动态载入数据的要求就会出现. 为此.该效果功能就须要应用 ...

  8. android使用PullToRefresh实现上拉加载和下拉刷新效果

    其实很早前就在博客园中也写过官方的下拉刷新控件SwipeRefreshLayout,但是这个控件仅仅支持下拉刷新,用起来还算可以.然而在我们实际开发应用中,很多地方都不止有下拉刷新,而且还有上拉加载的 ...

  9. &lbrack;Android&rsqb;下拉刷新控件RefreshableView的实现

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4172483.html 需求:自定义一个ViewGroup,实现 ...

随机推荐

  1. vi、vim 查找替换

    vi/vim 中可以使用 :s 命令来替换字符串.该命令有很多种不同细节使用方法,可以实现复杂的功能,记录几种在此,方便以后查询.    :s/vivian/sky/ 替换当前行第一个 vivian ...

  2. Objective C 快速入门学习一

    Objective-C程序设计 1. 直接用Xcode作为IDE,舍弃gcc编译方面的学习.2. 入门例子:Eg:打印Hello World 控制台程序 #import<Foundation/F ...

  3. 使用MyBatis Generator自动创建代码

    SSM框架--使用MyBatis Generator自动创建代码 1. 目录说明 使用自动生成有很多方式,可以在eclipse中安装插件,但是以下将要介绍的这种方式我认为很轻松,最简单,不需要装插件, ...

  4. Microsoft SQL Server,附加数据库 错误&colon;Error 916解决方法

    错误信息:错误提示:标题: Microsoft SQL Server Management Studio Express ——————————  无法为此请求检索数据. (Microsoft.SqlS ...

  5. 深圳尚学堂:Java中Class对象

    Java中生成Class对象和生成instance都有多种方式.所以只有弄清其中的原理,才可以深入理解.首先要生成Class对象,然后再生成Instance.那Class对象的生成方式有哪些呢,以及其 ...

  6. 轻量级集群管理软件-Ansible

    ansible概述和运行机制 ansible概述 Ansible是一款为类Unix系统开发的*开源的配置和自动化工具,  它用Python写成,类似于saltstack和Puppet,但是有一个不同 ...

  7. netty&lpar;二&rpar; 创建一个netty服务端和客户端

    服务端 NettyServer package com.zw.netty.config; import com.zw.netty.channel.ServerInitializer;import io ...

  8. 结对项目 Pair Project

    结对项目 Pair Project 一人编程,一人操作,共同检查. 源码 https://github.com/dpch16303/test/blob/master/%E5%AE%9E%E8%B7%B ...

  9. 【netcore入门】在Windows IIS上部署&period;NET Core 2&period;1项目

    部署之前先检查下面2个先决条件是否满足 1.安装了 IIS 模块 win7 在 控制面板→程序和功能→打开或关闭Windows功能→勾选Internet 信息服务(Internet Informati ...

  10. JDBC删除表实例

    在本教程将演示如何在JDBC应用程序中删除一个数据库表. 在执行以下示例之前,请确保您已经准备好以下操作: 具有数据库管理员权限,以在给定模式中删除数据库表. 要执行以下示例,需要用实际用户名和密码替 ...