本文主要包括以下内容

  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,RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout)

    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. [Swift通天遁地]二、表格表单-(4)使用系统自带的下拉刷新控件,制作表格的下拉刷新效果

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

  6. [Swift通天遁地]二、表格表单-(6)创建美观的表格弹性下拉刷新效果

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

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

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

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

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

  9. [Android]下拉刷新控件RefreshableView的实现

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

随机推荐

  1. formValidator的一些验证实例

    原帖地址:http://www.cnblogs.com/talk/archive/2012/01/29/2330887.html $(function () { try { $.formValidat ...

  2. Camel——涨知识了,骆驼命名法

    骆驼式命名法(Camel-Case)又称驼峰命名法,是电脑程式编写时的一套命名规则(惯例).正如它的名称CamelCase所表示的那样,是指混合使用大小写字母来构成变量和函数的名字.程序员们为了自己的 ...

  3. BZOJ1452——[JSOI2009]Count

    1.题目大意: 就是给一个n×m的方格,然后一些平面上的 求和 修改操作 2.分析:二维树状数组裸题 #include <cstdio> #include <cstdlib> ...

  4. Unity手游之路<二>Java版服务端使用protostuff简化protobuf开发

    http://blog.csdn.net/janeky/article/details/17151465 开发一款网络游戏,首先要考虑的是客户端服务端之间用何种编码格式进行通信.之前我们介绍了Unit ...

  5. 关于NGUI与原生2D混用相互遮盖的问题心得

    http://www.fzgh.org.cn/zuixindianying/144224.html Native2D自己可以使用Sort Layer来排序,每层又有不同的Order In Layer, ...

  6. 一颗躁动的心---下决心从SLAM开始,不钻研嵌入式底层了

    在写这个随笔时,北京的外面正在下2016的第一场雪.夜深人尽之时总会考虑一下自己的未来在何方. 长这么大了,我发现我这人始终不能坚定不移的朝着一个方向努力,总是朝三暮四,对学习更是朝令夕改,这造成了我 ...

  7. Android 中的 Intent 简介

    Intent是Android程序中各组件之间进行交互的一种重要方式,它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据. ------------------------------- ...

  8. linux shell脚本常用语句

    linux shell 指令 诸如-d, -f, -e之类的判断表达式: 文件比较运算符-e filename  如果 filename存在,则为真  [ -e /var/log/syslog ]-d ...

  9. POJ 1509 Glass Beads

    Description 求字符串的最小循环表示. Sol SAM. 把原串复制一遍,建出SAM,然后每次选最小的一个跑 \(len\) 次,这就是最小循环表示的最后一个节点,然后 \(x-len+1\ ...

  10. 微型Http服务器Tiny Http Server

    Tiny Http Server 一个简单的跨平台Http服务器.服务器部分使用了Mongoose的代码,界面是使用QT开发的. 开发为了在临时需要使用一个http服务器来做发布代码文档的时候,不用去 ...