先看效果图







Android 自定义下拉刷新_ide


使用这个自定义的下拉刷新 可以实现  任意布局的下拉刷新  ,例如我这边使用的是一个ListView 


使用方法就是 在布局文件中配制:

<RelativeLayout 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" tools:context=".MainActivity">
<com.example.administrator.friendscircleapplication.CircleRefreshLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
app:AniBackColor="#ff8b90af"
app:AniForeColor="#ffffffff"
app:CircleSmaller="6"
android:background="#e2e2e2"
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:background="#ff8b90af"
android:clipToPadding="false"
android:divider="@null"
android:fadingEdge="none"
android:dividerHeight="16dp"
android:padding="16dp"
android:scrollbarStyle="outsideOverlay"

android:id="@+id/friend_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</com.example.administrator.friendscircleapplication.CircleRefreshLayout>
<Button
android:id="@+id/stop_refresh"
android:text="Stop Refreshing"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />



</RelativeLayout>


当然这里使用的不是自定义的ListView 而是自定义的一个  FrameLayout


java代码中的实现


mRefreshLayout.setOnRefreshListener(new CircleRefreshLayout.OnCircleRefreshListener() {
@Override
public void completeRefresh() {

}

@Override
public void refreshing() {

}
});


mRefreshLayout.finishRefreshing();




相关文件的配制


values  -  attrs .xml中 

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CirCleRefreshLayout">
<attr name="AniBackColor" format="color" />
<attr name="AniForeColor" format="color" />
<attr name="CircleSmaller" format="integer"/>
</declare-styleable>
</resources>




在代码中使用的时候 可以直接将下面的代码 复制到项目中即可 

自定义FrameLayout的核心


import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;

/**
*
*/
public class CircleRefreshLayout extends FrameLayout {

private static String TAG = "pullToRefresh";

private static final long BACK_TOP_DUR = 600;
private static final long REL_DRAG_DUR = 200;

private int mHeaderBackColor = 0xff8b90af;
private int mHeaderForeColor = 0xffffffff;
private int mHeaderCircleSmaller = 6;


private float mPullHeight;
private float mHeaderHeight;
private View mChildView;
private AnimationView mHeader;

private boolean mIsRefreshing;

private float mTouchStartY;

private float mTouchCurY;

private ValueAnimator mUpBackAnimator;
private ValueAnimator mUpTopAnimator;

private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator(10);

public CircleRefreshLayout(Context context) {
this(context, null, 0);
}

public CircleRefreshLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public CircleRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}

private void init(Context context, AttributeSet attrs, int defStyleAttr) {

if (getChildCount() > 1) {
throw new RuntimeException("you can only attach one child");
}
setAttrs(attrs);
mPullHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150, context.getResources().getDisplayMetrics());
mHeaderHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, context.getResources().getDisplayMetrics());

this.post(new Runnable() {
@Override
public void run() {
mChildView = getChildAt(0);
addHeaderView();
}
});

}

private void setAttrs(AttributeSet attrs) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CirCleRefreshLayout);

mHeaderBackColor = a.getColor(R.styleable.CirCleRefreshLayout_AniBackColor, mHeaderBackColor);
mHeaderForeColor = a.getColor(R.styleable.CirCleRefreshLayout_AniForeColor, mHeaderForeColor);
mHeaderCircleSmaller = a.getInt(R.styleable.CirCleRefreshLayout_CircleSmaller, mHeaderCircleSmaller);

a.recycle();
}

private void addHeaderView() {
mHeader = new AnimationView(getContext());
LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
params.gravity = Gravity.TOP;
mHeader.setLayoutParams(params);

addViewInternal(mHeader);
mHeader.setAniBackColor(mHeaderBackColor);
mHeader.setAniForeColor(mHeaderForeColor);
mHeader.setRadius(mHeaderCircleSmaller);

setUpChildAnimation();
}

private void setUpChildAnimation() {
if (mChildView == null) {
return;
}
mUpBackAnimator = ValueAnimator.ofFloat(mPullHeight, mHeaderHeight);
mUpBackAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float val = (float) animation.getAnimatedValue();
if (mChildView != null) {
mChildView.setTranslationY(val);
}
}
});
mUpBackAnimator.setDuration(REL_DRAG_DUR);
mUpTopAnimator = ValueAnimator.ofFloat(mHeaderHeight, 0);
mUpTopAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float val = (float) animation.getAnimatedValue();
val = decelerateInterpolator.getInterpolation(val / mHeaderHeight) * val;
if (mChildView != null) {
mChildView.setTranslationY(val);
}
mHeader.getLayoutParams().height = (int) val;
mHeader.requestLayout();
}
});
mUpTopAnimator.setDuration(BACK_TOP_DUR);

mHeader.setOnViewAniDone(new AnimationView.OnViewAniDone() {
@Override
public void viewAniDone() {
// Log.i(TAG, "should invoke");
mUpTopAnimator.start();
}
});


}

private void addViewInternal(@NonNull View child) {
super.addView(child);
}

@Override
public void addView(View child) {
if (getChildCount() >= 1) {
throw new RuntimeException("you can only attach one child");
}

mChildView = child;
super.addView(child);
setUpChildAnimation();
}

private boolean canChildScrollUp() {
if (mChildView == null) {
return false;
}


return ViewCompat.canScrollVertically(mChildView, -1);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mIsRefreshing) {
return true;
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mTouchStartY = ev.getY();
mTouchCurY = mTouchStartY;
break;
case MotionEvent.ACTION_MOVE:
float curY = ev.getY();
float dy = curY - mTouchStartY;
if (dy > 0 && !canChildScrollUp()) {
return true;
}
}
return super.onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (mIsRefreshing) {
return super.onTouchEvent(event);
}

switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
mTouchCurY = event.getY();
float dy = mTouchCurY - mTouchStartY;
dy = Math.min(mPullHeight * 2, dy);
dy = Math.max(0, dy);


if (mChildView != null) {
float offsetY = decelerateInterpolator.getInterpolation(dy / 2 / mPullHeight) * dy / 2;
mChildView.setTranslationY(offsetY);

mHeader.getLayoutParams().height = (int) offsetY;
mHeader.requestLayout();
}


return true;

case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mChildView != null) {
if (mChildView.getTranslationY() >= mHeaderHeight) {
mUpBackAnimator.start();
mHeader.releaseDrag();
mIsRefreshing = true;
if (onCircleRefreshListener!=null) {
onCircleRefreshListener.refreshing();
}

} else {
float height = mChildView.getTranslationY();
ValueAnimator backTopAni = ValueAnimator.ofFloat(height, 0);
backTopAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float val = (float) animation.getAnimatedValue();
val = decelerateInterpolator.getInterpolation(val / mHeaderHeight) * val;
if (mChildView != null) {
mChildView.setTranslationY(val);
}
mHeader.getLayoutParams().height = (int) val;
mHeader.requestLayout();
}
});
backTopAni.setDuration((long) (height * BACK_TOP_DUR / mHeaderHeight));
backTopAni.start();
}
}
return true;
default:
return super.onTouchEvent(event);
}
}

public void finishRefreshing() {
if (onCircleRefreshListener != null) {
onCircleRefreshListener.completeRefresh();
}
mIsRefreshing = false;
mHeader.setRefreshing(false);
}

private OnCircleRefreshListener onCircleRefreshListener;

public void setOnRefreshListener(OnCircleRefreshListener onCircleRefreshListener) {
this.onCircleRefreshListener = onCircleRefreshListener;
}

public interface OnCircleRefreshListener {
void completeRefresh();

void refreshing();
}
}


配合另一个类使用  


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;

/**
*
*/
public class AnimationView extends View {

private static final String TAG = "AnimationView";

private int PULL_HEIGHT;
private int PULL_DELTA;
private float mWidthOffset;



private AnimatorStatus mAniStatus = AnimatorStatus.PULL_DOWN;

enum AnimatorStatus {
PULL_DOWN,
DRAG_DOWN,
REL_DRAG,
SPRING_UP, // rebound to up, the position is less than PULL_HEIGHT
POP_BALL,
OUTER_CIR,
REFRESHING,
DONE,
STOP;

@Override
public String toString() {
switch (this) {
case PULL_DOWN:
return "pull down";
case DRAG_DOWN:
return "drag down";
case REL_DRAG:
return "release drag";
case SPRING_UP:
return "spring up";
case POP_BALL:
return "pop ball";
case OUTER_CIR:
return "outer circle";
case REFRESHING:
return "refreshing...";
case DONE:
return "done!";
case STOP:
return "stop";
default:
return "unknown state";
}
}
}


private Paint mBackPaint;
private Paint mBallPaint;
private Paint mOutPaint;
private Path mPath;


public AnimationView(Context context) {
this(context, null, 0);
}

public AnimationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs, defStyleAttr);
}

private void initView(Context context, AttributeSet attrs, int defStyleAttr) {

PULL_HEIGHT = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, context.getResources().getDisplayMetrics());
PULL_DELTA = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics());
mWidthOffset = 0.5f;
mBackPaint = new Paint();
mBackPaint.setAntiAlias(true);
mBackPaint.setStyle(Paint.Style.FILL);
mBackPaint.setColor(0xff8b90af);

mBallPaint = new Paint();
mBallPaint.setAntiAlias(true);
mBallPaint.setColor(0xffffffff);
mBallPaint.setStyle(Paint.Style.FILL);

mOutPaint = new Paint();
mOutPaint.setAntiAlias(true);
mOutPaint.setColor(0xffffffff);
mOutPaint.setStyle(Paint.Style.STROKE);
mOutPaint.setStrokeWidth(5);


mPath = new Path();

}

private int mRadius;
private int mWidth;
private int mHeight;

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
if (height > PULL_DELTA + PULL_HEIGHT) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(PULL_DELTA + PULL_HEIGHT, MeasureSpec.getMode(heightMeasureSpec));
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
mRadius = getHeight() / 6;
mWidth = getWidth();
mHeight = getHeight();

if (mHeight < PULL_HEIGHT) {
mAniStatus = AnimatorStatus.PULL_DOWN;
}


switch (mAniStatus) {
case PULL_DOWN:
if (mHeight >= PULL_HEIGHT) {
mAniStatus = AnimatorStatus.DRAG_DOWN;
}
break;
case REL_DRAG:
break;
}

}
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

switch (mAniStatus) {
case PULL_DOWN:
canvas.drawRect(0, 0, mWidth, mHeight, mBackPaint);
break;
case REL_DRAG:
case DRAG_DOWN:
drawDrag(canvas);
break;
case SPRING_UP:
drawSpring(canvas, getSpringDelta());
invalidate();
break;
case POP_BALL:
drawPopBall(canvas);
invalidate();
break;
case OUTER_CIR:
drawOutCir(canvas);
invalidate();
break;
case REFRESHING:
drawRefreshing(canvas);
invalidate();
break;
case DONE:
drawDone(canvas);
invalidate();
break;
case STOP:
drawDone(canvas);
break;

}

if (mAniStatus == AnimatorStatus.REL_DRAG) {
ViewGroup.LayoutParams params = getLayoutParams();
int height;
// NOTICE: If the height equals mLastHeight, then the requestLayout() will not work correctly
do {
height = getRelHeight();
} while (height == mLastHeight && getRelRatio() != 1);
mLastHeight = height;
params.height = PULL_HEIGHT + height;
requestLayout();
}


}

private void drawDrag(Canvas canvas) {
canvas.drawRect(0, 0, mWidth, PULL_HEIGHT, mBackPaint);

mPath.reset();
mPath.moveTo(0, PULL_HEIGHT);
mPath.quadTo(mWidthOffset * mWidth, PULL_HEIGHT + (mHeight - PULL_HEIGHT) * 2,
mWidth, PULL_HEIGHT);
canvas.drawPath(mPath, mBackPaint);
}

private void drawSpring(Canvas canvas, int springDelta) {
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(0, PULL_HEIGHT);
mPath.quadTo(mWidth / 2, PULL_HEIGHT - springDelta,
mWidth, PULL_HEIGHT);
mPath.lineTo(mWidth, 0);
canvas.drawPath(mPath, mBackPaint);

int curH = PULL_HEIGHT - springDelta / 2;

if (curH > PULL_HEIGHT - PULL_DELTA / 2) {
int leftX = (int) (mWidth / 2 - 2 * mRadius + getSprRatio() * mRadius);
mPath.reset();
mPath.moveTo(leftX, curH);
mPath.quadTo(mWidth / 2, curH - mRadius * getSprRatio() * 2,
mWidth - leftX, curH);
canvas.drawPath(mPath, mBallPaint);
} else {
canvas.drawArc(new RectF(mWidth / 2 - mRadius, curH - mRadius, mWidth / 2 + mRadius, curH + mRadius),
180, 180, true, mBallPaint);
}

}

private void drawPopBall(Canvas canvas) {
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(0, PULL_HEIGHT);
mPath.quadTo(mWidth / 2, PULL_HEIGHT - PULL_DELTA,
mWidth, PULL_HEIGHT);
mPath.lineTo(mWidth, 0);
canvas.drawPath(mPath, mBackPaint);

int cirCentStart = PULL_HEIGHT - PULL_DELTA / 2;
int cirCenY = (int) (cirCentStart - mRadius * 2 * getPopRatio());

canvas.drawArc(new RectF(mWidth / 2 - mRadius, cirCenY - mRadius, mWidth / 2 + mRadius, cirCenY + mRadius),
180, 360, true, mBallPaint);

if (getPopRatio() < 1) {
drawTail(canvas, cirCenY, cirCentStart + 1, getPopRatio());
} else {
canvas.drawCircle(mWidth / 2, cirCenY, mRadius, mBallPaint);
}


}

private void drawTail(Canvas canvas, int centerY, int bottom, float fraction) {
int bezier1w = (int) (mWidth / 2 + (mRadius * 3 / 4) * (1 - fraction));
PointF start = new PointF(mWidth / 2 + mRadius, centerY);
PointF bezier1 = new PointF(bezier1w, bottom);
PointF bezier2 = new PointF(bezier1w + mRadius / 2, bottom);

mPath.reset();
mPath.moveTo(start.x, start.y);
mPath.quadTo(bezier1.x, bezier1.y,
bezier2.x, bezier2.y);
mPath.lineTo(mWidth - bezier2.x, bezier2.y);
mPath.quadTo(mWidth - bezier1.x, bezier1.y,
mWidth - start.x, start.y);
canvas.drawPath(mPath, mBallPaint);
}

private void drawOutCir(Canvas canvas) {
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(0, PULL_HEIGHT);
mPath.quadTo(mWidth / 2, PULL_HEIGHT - (1 - getOutRatio()) * PULL_DELTA,
mWidth, PULL_HEIGHT);
mPath.lineTo(mWidth, 0);
canvas.drawPath(mPath, mBackPaint);
int innerY = PULL_HEIGHT - PULL_DELTA / 2 - mRadius * 2;
canvas.drawCircle(mWidth / 2, innerY, mRadius, mBallPaint);
}

private int mRefreshStart = 90;
private int mRefreshStop = 90;
private int TARGET_DEGREE = 270;
private boolean mIsStart = true;
private boolean mIsRefreshing = true;

private void drawRefreshing(Canvas canvas) {
canvas.drawRect(0, 0, mWidth, mHeight, mBackPaint);
int innerY = PULL_HEIGHT - PULL_DELTA / 2 - mRadius * 2;
canvas.drawCircle(mWidth / 2, innerY, mRadius, mBallPaint);
int outerR = mRadius + 10;

mRefreshStart += mIsStart ? 3 : 10;
mRefreshStop += mIsStart ? 10 : 3;
mRefreshStart = mRefreshStart % 360;
mRefreshStop = mRefreshStop % 360;

int swipe = mRefreshStop - mRefreshStart;
swipe = swipe < 0 ? swipe + 360 : swipe;

canvas.drawArc(new RectF(mWidth / 2 - outerR, innerY - outerR, mWidth / 2 + outerR, innerY + outerR),
mRefreshStart, swipe, false, mOutPaint);
if (swipe >= TARGET_DEGREE) {
mIsStart = false;
} else if (swipe <= 10) {
mIsStart = true;
}
if (!mIsRefreshing) {
applyDone();

}

}

// stop refreshing
public void setRefreshing(boolean isFresh) {
mIsRefreshing = isFresh;
}

private void drawDone(Canvas canvas) {


int beforeColor = mOutPaint.getColor();
if (getDoneRatio() < 0.3) {
canvas.drawRect(0, 0, mWidth, mHeight, mBackPaint);
int innerY = PULL_HEIGHT - PULL_DELTA / 2 - mRadius * 2;
canvas.drawCircle(mWidth / 2, innerY, mRadius, mBallPaint);
int outerR = (int) (mRadius + 10 + 10 * getDoneRatio() / 0.3f);
int afterColor = Color.argb((int) (0xff * (1 - getDoneRatio() / 0.3f)), Color.red(beforeColor),
Color.green(beforeColor), Color.blue(beforeColor));
mOutPaint.setColor(afterColor);
canvas.drawArc(new RectF(mWidth / 2 - outerR, innerY - outerR, mWidth / 2 + outerR, innerY + outerR),
0, 360, false, mOutPaint);
}
mOutPaint.setColor(beforeColor);


if (getDoneRatio() >= 0.3 && getDoneRatio() < 0.7) {
canvas.drawRect(0, 0, mWidth, mHeight, mBackPaint);
float fraction = (getDoneRatio() - 0.3f) / 0.4f;
int startCentY = PULL_HEIGHT - PULL_DELTA / 2 - mRadius * 2;
int curCentY = (int) (startCentY + (PULL_DELTA / 2 + mRadius * 2) * fraction);
canvas.drawCircle(mWidth / 2, curCentY, mRadius, mBallPaint);
if (curCentY >= PULL_HEIGHT - mRadius * 2) {
drawTail(canvas, curCentY, PULL_HEIGHT, (1 - fraction));
}
}

if (getDoneRatio() >= 0.7 && getDoneRatio() <= 1) {
float fraction = (getDoneRatio() - 0.7f) / 0.3f;
canvas.drawRect(0, 0, mWidth, mHeight, mBackPaint);
int leftX = (int) (mWidth / 2 - mRadius - 2 * mRadius * fraction);
mPath.reset();
mPath.moveTo(leftX, PULL_HEIGHT);
mPath.quadTo(mWidth / 2, PULL_HEIGHT - (mRadius * (1 - fraction)),
mWidth - leftX, PULL_HEIGHT);
canvas.drawPath(mPath, mBallPaint);
}

}

private int mLastHeight;

private int getRelHeight() {
return (int) (mSpriDeta * (1 - getRelRatio()));
}

private int getSpringDelta() {
return (int) (PULL_DELTA * getSprRatio());
}


private static long REL_DRAG_DUR = 200;

private long mStart;
private long mStop;
private int mSpriDeta;

public void releaseDrag() {
mStart = System.currentTimeMillis();
mStop = mStart + REL_DRAG_DUR;
mAniStatus = AnimatorStatus.REL_DRAG;
mSpriDeta = mHeight - PULL_HEIGHT;
requestLayout();
}

private float getRelRatio() {
if (System.currentTimeMillis() >= mStop) {
springUp();
return 1;
}
float ratio = (System.currentTimeMillis() - mStart) / (float) REL_DRAG_DUR;
return Math.min(ratio, 1);
}

private static long SPRING_DUR = 200;
private long mSprStart;
private long mSprStop;


private void springUp() {
mSprStart = System.currentTimeMillis();
mSprStop = mSprStart + SPRING_DUR;
mAniStatus = AnimatorStatus.SPRING_UP;
invalidate();
}


private float getSprRatio() {
if (System.currentTimeMillis() >= mSprStop) {
popBall();
return 1;
}
float ratio = (System.currentTimeMillis() - mSprStart) / (float) SPRING_DUR;
return Math.min(1, ratio);
}

private static final long POP_BALL_DUR = 300;
private long mPopStart;
private long mPopStop;

private void popBall() {
mPopStart = System.currentTimeMillis();
mPopStop = mPopStart + POP_BALL_DUR;
mAniStatus = AnimatorStatus.POP_BALL;
invalidate();
}

private float getPopRatio() {
if (System.currentTimeMillis() >= mPopStop) {
startOutCir();
return 1;
}

float ratio = (System.currentTimeMillis() - mPopStart) / (float) POP_BALL_DUR;
return Math.min(ratio, 1);
}

private static final long OUTER_DUR = 200;
private long mOutStart;
private long mOutStop;

private void startOutCir() {
mOutStart = System.currentTimeMillis();
mOutStop = mOutStart + OUTER_DUR;
mAniStatus = AnimatorStatus.OUTER_CIR;
mRefreshStart = 90;
mRefreshStop = 90;
TARGET_DEGREE = 270;
mIsStart = true;
mIsRefreshing = true;
invalidate();
}

private float getOutRatio() {
if (System.currentTimeMillis() >= mOutStop) {
mAniStatus = AnimatorStatus.REFRESHING;
mIsRefreshing = true;
return 1;
}
float ratio = (System.currentTimeMillis() - mOutStart) / (float) OUTER_DUR;
return Math.min(ratio, 1);
}

private static final long DONE_DUR = 1000;
private long mDoneStart;
private long mDoneStop;

private void applyDone() {
mDoneStart = System.currentTimeMillis();
mDoneStop = mDoneStart + DONE_DUR;
mAniStatus = AnimatorStatus.DONE;
}

private float getDoneRatio() {
if (System.currentTimeMillis() >= mDoneStop) {
mAniStatus = AnimatorStatus.STOP;
if (onViewAniDone != null) {
onViewAniDone.viewAniDone();
}
return 1;
}

float ratio = (System.currentTimeMillis() - mDoneStart) / (float) DONE_DUR;
return Math.min(ratio, 1);
}


private OnViewAniDone onViewAniDone;

public void setOnViewAniDone(OnViewAniDone onViewAniDone) {
this.onViewAniDone = onViewAniDone;
}

interface OnViewAniDone {
void viewAniDone();
}


public void setAniBackColor(int color) {
mBackPaint.setColor(color);
}

public void setAniForeColor(int color) {
mBallPaint.setColor(color);
mOutPaint.setColor(color);
setBackgroundColor(color);
}

// the height of view is smallTimes times of circle radius
public void setRadius(int smallTimes) {
mRadius = mHeight / smallTimes;
}


}