Android下拉刷新已经被很多人写过了,网上的开源代码也很多,写这篇文章只是想记录一下自己学习的过程。
首先自定义一个下拉刷新的布局layout,布局分为2部分,一部分是开发者自己添加的layout、占据整个屏幕,一部分是隐藏在屏幕上方的刷新headview。下拉的时候把headview显示出来,实现下拉刷新的效果。
首先我定义了一个抽象类
public abstract class HeadLayout {
//得到headview
public abstract View getHeadView();
//设置下拉刷新过程中headview的状态
public abstract void setHeadViewStatus(Context context, View headView, int status);
}
getHeadView() 就是得到头部的headview。
setHeadViewStatus() 就是设置下拉 过程当中headview的状态变更
本例中我实现了2个类
public class OrdinaryHeadLayout extends HeadLayout {
private View headView;
public OrdinaryHeadLayout(View headView) {
this.headView = headView;
}
@Override
public View getHeadView() {
return headView;
}
@Override
public void setHeadViewStatus(Context context, View headView, int status) {
if(context == null || headView == null) return;
TextView textView = (TextView) headView.findViewById(R.id.text);
switch (status) {
case PullDownRefreshLayout.INIT_STATUS:
textView.setText("下拉刷新");
break;
case PullDownRefreshLayout.TIP_STATUS:
textView.setText("释放刷新");
break;
case PullDownRefreshLayout.REFRESHING_STATUS:
textView.setText("正在刷新");
break;
case PullDownRefreshLayout.REFRESHING_END_STATUS:
textView.setText("下拉刷新");
break;
}
}
}
在下拉过程当中这个就是简单的设置textview的内容,在每个状态设置不同的内容。
public class PersonalityHeadLayout extends HeadLayout {
private View headView;
private Integer[] image = {R.drawable.icon_loading_one, R.drawable.icon_loading_two, R.drawable.icon_loading_three, R.drawable.icon_loading_four, R.drawable.icon_loading_five,
R.drawable.icon_loading_six, R.drawable.icon_loading_seven, R.drawable.icon_loading_eight};
private int height;
private AnimationDrawable animationDrawable;
public PersonalityHeadLayout(View headView) {
this.headView = headView;
}
@Override
public View getHeadView() {
return headView;
}
@Override
public void setHeadViewStatus(Context context, View headView, int status) {
if(context == null || headView == null) return;
if(height == 0)
height = headView.getMeasuredHeight() / 8;
ImageView imageView = (ImageView) headView.findViewById(R.id.image);
switch (status) {
case PullDownRefreshLayout.INIT_STATUS:
if(headView.getTop() > -headView.getMeasuredHeight() && headView.getTop() < -headView.getMeasuredHeight() + height) {
imageView.setImageResource(R.drawable.icon_loading_one);
}
if(headView.getTop() > -headView.getMeasuredHeight() + height && headView.getTop() < -headView.getMeasuredHeight() + height * 2) {
imageView.setImageResource(R.drawable.icon_loading_two);
}
if(headView.getTop() > -headView.getMeasuredHeight() + height * 2 && headView.getTop() < -headView.getMeasuredHeight() + height * 3) {
imageView.setImageResource(R.drawable.icon_loading_three);
}
if(headView.getTop() > -headView.getMeasuredHeight() + height * 3 && headView.getTop() < -headView.getMeasuredHeight() + height * 4) {
imageView.setImageResource(R.drawable.icon_loading_four);
}
if(headView.getTop() > -headView.getMeasuredHeight() + height * 4 && headView.getTop() < -headView.getMeasuredHeight() + height * 5) {
imageView.setImageResource(R.drawable.icon_loading_five);
}
if(headView.getTop() > -headView.getMeasuredHeight() + height * 5 && headView.getTop() < -headView.getMeasuredHeight() + height * 6) {
imageView.setImageResource(R.drawable.icon_loading_six);
}
if(headView.getTop() > -headView.getMeasuredHeight() + height * 6 && headView.getTop() < -headView.getMeasuredHeight() + height * 7) {
imageView.setImageResource(R.drawable.icon_loading_seven);
}
if(headView.getTop() > -headView.getMeasuredHeight() + height * 7 && headView.getTop() < -headView.getMeasuredHeight() + height * 8) {
imageView.setImageResource(R.drawable.icon_loading_eight);
}
break;
case PullDownRefreshLayout.TIP_STATUS:
imageView.setImageResource(R.drawable.icon_loading_eight);
break;
case PullDownRefreshLayout.REFRESHING_STATUS:
animationDrawable = (AnimationDrawable) context.getResources().getDrawable(R.drawable.loading);
imageView.setImageDrawable(animationDrawable);
animationDrawable.start();
break;
case PullDownRefreshLayout.REFRESHING_END_STATUS:
if(animationDrawable == null) return;
animationDrawable.stop();
animationDrawable = null;
break;
}
}
}
这个就是一个图片动态的动画效果的实现。
通过
public void setHeadLayout(HeadLayout headLayout) {
this.headLayout = headLayout;
mHead.addView(headLayout.getHeadView(), new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
}
方法来设置不同的HeadLayout来实现不同的动画效果
下来就要自定义PullDownRefreshLayout
package com.qingcong.pulldownrefresh;
import android.animation.IntEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.LinearLayout;
/**
* Created by chenpengfei on 2016/4/25.
* 下拉刷新
* HeadLayout 抽象类是用来做自定义headview效果的,实现HeadLayout 抽象类,实现里面的方法来定义属于自己的动画效果
* 本例子实现了2中效果,普通效果的OrdinaryHeadLayout, 个性效果PersonalityHeadLayout
* 本例实现逻辑是:
* 初始化view的时候添加一个空的linearlayout类,用来添加自定义的headview,通过实现HeadLayout抽象类来添加headview,详情查看setHeadLayout()方法
* 手指滑动触摸屏幕的时候重写了拦截方法,判断如果当前view正在执行动画或者当前view还没有滚动到最顶部还可以向下滚动,就不拦截事件,否则拦截事件
* 拦截以后在onTouchEvent实现滑动逻辑,计算滑动的总距离,然后requestLayout();方法通知布局重绘,通过不断的调用view的layout方法来实现动画的视觉效果
*/
public class PullDownRefreshLayout extends ViewGroup {
public final static int INIT_STATUS = 0; //初始状态
public final static int TIP_STATUS = 1; //提示状态
public final static int REFRESHING_STATUS = 2; //正在刷新
public final static int REFRESHING_END_STATUS = 3; //刷新完成状态
private HeadLayout headLayout;
//是否正在拖
private boolean mIsBeingDragged;
//是否正在刷新
private boolean mIsRefreshing;
//是否正在重置
private boolean mIsReturnToStart;
//正准备滑动到加载转态
private boolean mScrollToLoading;
//拖的对象
private View mTarget;
//head view
private LinearLayout mHead;
// 检测到手机的最小滑动值
private int mTouchSlop;
private float mInitialMotionY;
private float mInitalMotionX;
//拖动的总大小
private float offsetSumTop;
private OnRefreshListener onRefreshListener;
private Context context;
private ValueAnimator valueAnimator;
public PullDownRefreshLayout(Context context) {
super(context);
}
public PullDownRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
addView(createHeadLayout(context));
}
public PullDownRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 创建一个空的linearlayout,用来添加自定义的headview,不同的headview会呈现不同的滑动视觉
*/
private LinearLayout createHeadLayout(Context context) {
LinearLayout linearLayout = new LinearLayout(context);
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
linearLayout.setTag("head");
linearLayout.setLayoutParams(layoutParams);
return linearLayout;
}
/**
* 设置自定义headview的实现滑动效果类
*/
public void setHeadLayout(HeadLayout headLayout) {
this.headLayout = headLayout;
mHead.addView(headLayout.getHeadView(), new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
}
/**
* 得到拖的目标view
*/
private void obtainTarget() {
if(mTarget == null) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
if(childView.getTag() == null) {
mTarget = childView;
} else {
//得到headview
mHead = (LinearLayout) childView;
}
}
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
obtainTarget();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(mTarget == null)
obtainTarget();
if(mTarget == null)
return;
mHead.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY),
getChildMeasureSpec(heightMeasureSpec, 0, mHead.getLayoutParams().height));
mTarget.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int mHleft = getPaddingLeft();
final int mTleft = getPaddingLeft();
int mHeadTop = (int) (-mHead.getMeasuredHeight() + offsetSumTop);
if(mHeadTop < -mHead.getMeasuredHeight()) {
mHeadTop = -mHead.getMeasuredHeight();
}
mHead.layout(mHleft, mHeadTop, mHleft + mHead.getMeasuredWidth(), mHeadTop + mHead.getMeasuredHeight());
mTarget.layout(mTleft, mHead.getBottom(), mTleft + mTarget.getMeasuredWidth(), mTarget.getMeasuredHeight() + mHead.getBottom());
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
obtainTarget();
//如果下拉操作在执行就不拦截
if(mIsRefreshing || mIsReturnToStart || mScrollToLoading || canChildScrollUp()) {
return false;
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
offsetSumTop = 0;
mIsBeingDragged = false;
mInitialMotionY = ev.getY();
mInitalMotionX = ev.getX();
break;
case MotionEvent.ACTION_MOVE:
final float y = ev.getY();
final float yDiff = y - mInitialMotionY;
final float x = ev.getX();
final float xDiff = x - mInitalMotionX;
//如果是X轴事件就不拦截
if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
mIsBeingDragged = false;
} else {
if (yDiff > mTouchSlop && !mIsBeingDragged) {
mInitialMotionY = mInitialMotionY + yDiff;
mIsBeingDragged = true;
}
}
break;
}
return mIsBeingDragged;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
final float y = event.getY();
final float yDiff = (y - mInitialMotionY) / 2;
offsetSumTop += yDiff;
mInitialMotionY = mInitialMotionY + yDiff * 2;
headLayout.setHeadViewStatus(context, mHead, mHead.getTop() >=0 ? TIP_STATUS : INIT_STATUS);
//发起绘制
requestLayout();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if(mHead.getTop() > 0) {
mScrollToLoading = true;
}
if(mHead.getTop() <= 0) {
mIsReturnToStart = true;
}
performAnimate(mHead.getTop(), mHead.getTop() > 0 ? 0 : -mHead.getMeasuredHeight());
break;
}
return true;
}
/**
* 滑动动画
*/
private void performAnimate(final int start, final int end) {
valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private IntEvaluator mEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animator) {
if(mHead == null || mTarget == null) return;
//获得当前动画的进度值,整型,1-100之间
int currentValue = (Integer) animator.getAnimatedValue();
float fraction = currentValue / 100f;
int top = mEvaluator.evaluate(fraction, start, end);
mHead.layout(mHead.getLeft(), top, mHead.getRight(), mHead.getMeasuredHeight() + top);
mTarget.layout(mTarget.getLeft(), mHead.getBottom(), mTarget.getRight(), mHead.getBottom() + mTarget.getMeasuredHeight());
setAnimationEndStatus(currentValue, end);
}
});
valueAnimator.setDuration(Math.abs(start - end) / mTouchSlop * 20).start();
}
/**
* 还原到初始状态位置
*/
public void returnToStart() {
mIsRefreshing = false;
mIsReturnToStart = true;
performAnimate(mHead.getTop(), - mHead.getMeasuredHeight());
}
/**
* 设置动画结束后参数的变更
*/
public void setAnimationEndStatus(int currentValue, int end) {
if(currentValue == 100) {
if(end == 0) {
if(onRefreshListener != null) {
mIsRefreshing = true;
mScrollToLoading = false;
offsetSumTop = mHead.getMeasuredHeight();
headLayout.setHeadViewStatus(context, mHead, REFRESHING_STATUS);
onRefreshListener.onRefresh();
}
} else {
mIsReturnToStart = false;
offsetSumTop = 0;
headLayout.setHeadViewStatus(context, mHead, REFRESHING_END_STATUS);
}
valueAnimator = null;
}
}
/**
* 还原所有参数
*/
public void destory() {
if(valueAnimator != null) {
valueAnimator.cancel();
valueAnimator = null;
}
offsetSumTop = 0;
headLayout.setHeadViewStatus(context, mHead, REFRESHING_END_STATUS);
requestLayout();
mIsRefreshing = false;
mIsReturnToStart = false;
mScrollToLoading = false;
mIsBeingDragged = false;
offsetSumTop = 0;
}
/**
* 是否滚动到了顶端
*/
public boolean canChildScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTarget instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTarget;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return ViewCompat.canScrollVertically(mTarget, -1) || mTarget.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTarget, -1);
}
}
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
this.onRefreshListener = onRefreshListener;
}
public interface OnRefreshListener {
public void onRefresh();
}
}
有不对的地方希望大家可以说出来,一起进步
代码地址