现在的位置: 首页 > 综合 > 正文

圆形进度条动画效果实现

2018年08月27日 ⁄ 综合 ⁄ 共 13129字 ⁄ 字号 评论关闭

转自 :http://blog.chengyunfeng.com/?p=607

在 G+ 上看到了一个不错的圆形进度条动画。
下图是该动画效果的演示:

 

 

 

 

 

 

 

 

是通过两个分别控制圆弧起始绘制位置和圆弧臂长的动画来实现的。
代码如下,实现原理在注释中由说明:



import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.util.Property;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;


public class CircularProgressDrawable extends Drawable implements Animatable {
<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 绘制圆弧起始位置角度的动画,这样该圆弧是打圈转的动画
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private static final Interpolator ANGLE_INTERPOLATOR = new LinearInterpolator();
<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 绘制圆弧臂长的动画,该动画受 mModeAppearing 控制, 当 mModeAppearing 为 false
<span style="white-space:pre">	</span> * 的时候,圆弧的起始点在增加,圆弧的终止点不变,弧长在逐渐减少; 当 mModeAppearing 为 true 的时候,
<span style="white-space:pre">	</span> * 圆弧的起始点不变,圆弧的终止点变大,弧长在逐渐增加
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private static final Interpolator SWEEP_INTERPOLATOR = new DecelerateInterpolator();
<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 圆弧起始位置动画的间隔,也就是多少毫秒圆弧转一圈,可以把该值扩大10倍来查看动画的慢动作
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private static final int ANGLE_ANIMATOR_DURATION = 20000;
<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 圆弧臂长的动画间隔,也就是臂长从最小到最大值的变化时间,也可以把该值扩大10倍来查看动画的慢动作
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private static final int SWEEP_ANIMATOR_DURATION = 6000;
<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 圆弧的最下臂长是多少度
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private static final int MIN_SWEEP_ANGLE = 30;
<span style="white-space:pre">	</span>private final RectF fBounds = new RectF();


<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 起始位置的动画对象
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private ObjectAnimator mObjectAnimatorSweep;
<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 臂长的动画对象
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private ObjectAnimator mObjectAnimatorAngle;
<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 控制臂长是逐渐增加还是逐渐减少
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private boolean mModeAppearing;
<span style="white-space:pre">	</span>private Paint mPaint;
<span style="white-space:pre">	</span>/**
<span style="white-space:pre">	</span> * 每次臂长增加 、减少 转换的时候, 圆弧起始位置的偏移量会增加 2 倍的最小臂长
<span style="white-space:pre">	</span> */
<span style="white-space:pre">	</span>private float mCurrentGlobalAngleOffset;
<span style="white-space:pre">	</span>private float mCurrentGlobalAngle;
<span style="white-space:pre">	</span>private float mCurrentSweepAngle;
<span style="white-space:pre">	</span>private float mBorderWidth;
<span style="white-space:pre">	</span>private boolean mRunning;


<span style="white-space:pre">	</span>public CircularProgressDrawable(int color, float borderWidth) {
<span style="white-space:pre">		</span>mBorderWidth = borderWidth;


<span style="white-space:pre">		</span>mPaint = new Paint();
<span style="white-space:pre">		</span>mPaint.setAntiAlias(true);
<span style="white-space:pre">		</span>mPaint.setStyle(Paint.Style.STROKE);
<span style="white-space:pre">		</span>mPaint.setStrokeWidth(borderWidth);
<span style="white-space:pre">		</span>mPaint.setColor(color);


<span style="white-space:pre">		</span>setupAnimations();
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>public void draw(Canvas canvas) {
<span style="white-space:pre">		</span>float startAngle = mCurrentGlobalAngle - mCurrentGlobalAngleOffset;
<span style="white-space:pre">		</span>float sweepAngle = mCurrentSweepAngle;
<span style="white-space:pre">		</span>if (mModeAppearing) {
<span style="white-space:pre">			</span>sweepAngle += MIN_SWEEP_ANGLE;
<span style="white-space:pre">		</span>} else {
<span style="white-space:pre">			</span>startAngle = startAngle + sweepAngle;
<span style="white-space:pre">			</span>sweepAngle = 360 - sweepAngle - MIN_SWEEP_ANGLE;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>canvas.drawArc(fBounds, startAngle, sweepAngle, false, mPaint);
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>public void setAlpha(int alpha) {
<span style="white-space:pre">		</span>mPaint.setAlpha(alpha);
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>public void setColorFilter(ColorFilter cf) {
<span style="white-space:pre">		</span>mPaint.setColorFilter(cf);
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>public int getOpacity() {
<span style="white-space:pre">		</span>return PixelFormat.TRANSPARENT;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>private void toggleAppearingMode() {
<span style="white-space:pre">		</span>mModeAppearing = !mModeAppearing;
<span style="white-space:pre">		</span>if (mModeAppearing) {
<span style="white-space:pre">			</span>mCurrentGlobalAngleOffset = (mCurrentGlobalAngleOffset + MIN_SWEEP_ANGLE * 2) % 360;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>protected void onBoundsChange(Rect bounds) {
<span style="white-space:pre">		</span>super.onBoundsChange(bounds);
<span style="white-space:pre">		</span>fBounds.left = bounds.left + mBorderWidth / 2f + .5f;
<span style="white-space:pre">		</span>fBounds.right = bounds.right - mBorderWidth / 2f - .5f;
<span style="white-space:pre">		</span>fBounds.top = bounds.top + mBorderWidth / 2f + .5f;
<span style="white-space:pre">		</span>fBounds.bottom = bounds.bottom - mBorderWidth / 2f - .5f;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>// ////////////////////////////////////////////////////////////////////////////
<span style="white-space:pre">	</span>// ////////////// Animation


<span style="white-space:pre">	</span>private Property<CircularProgressDrawable, Float> mAngleProperty = new Property<CircularProgressDrawable, Float>(
<span style="white-space:pre">			</span>Float.class, "angle") {
<span style="white-space:pre">		</span>@Override
<span style="white-space:pre">		</span>public Float get(CircularProgressDrawable object) {
<span style="white-space:pre">			</span>return object.getCurrentGlobalAngle();
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>@Override
<span style="white-space:pre">		</span>public void set(CircularProgressDrawable object, Float value) {
<span style="white-space:pre">			</span>object.setCurrentGlobalAngle(value);
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>};


<span style="white-space:pre">	</span>private Property<CircularProgressDrawable, Float> mSweepProperty = new Property<CircularProgressDrawable, Float>(
<span style="white-space:pre">			</span>Float.class, "arc") {
<span style="white-space:pre">		</span>@Override
<span style="white-space:pre">		</span>public Float get(CircularProgressDrawable object) {
<span style="white-space:pre">			</span>return object.getCurrentSweepAngle();
<span style="white-space:pre">		</span>}


<span style="white-space:pre">		</span>@Override
<span style="white-space:pre">		</span>public void set(CircularProgressDrawable object, Float value) {
<span style="white-space:pre">			</span>object.setCurrentSweepAngle(value);
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>};


<span style="white-space:pre">	</span>private void setupAnimations() {
<span style="white-space:pre">		</span>mObjectAnimatorAngle = ObjectAnimator.ofFloat(this, mAngleProperty,
<span style="white-space:pre">				</span>360f);
<span style="white-space:pre">		</span>mObjectAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR);
<span style="white-space:pre">		</span>mObjectAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION);
<span style="white-space:pre">		</span>mObjectAnimatorAngle.setRepeatMode(ValueAnimator.RESTART);
<span style="white-space:pre">		</span>mObjectAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE);


<span style="white-space:pre">		</span>mObjectAnimatorSweep = ObjectAnimator.ofFloat(this, mSweepProperty,
<span style="white-space:pre">				</span>360f - MIN_SWEEP_ANGLE * 2);
<span style="white-space:pre">		</span>mObjectAnimatorSweep.setInterpolator(SWEEP_INTERPOLATOR);
<span style="white-space:pre">		</span>mObjectAnimatorSweep.setDuration(SWEEP_ANIMATOR_DURATION);
<span style="white-space:pre">		</span>mObjectAnimatorSweep.setRepeatMode(ValueAnimator.RESTART);
<span style="white-space:pre">		</span>mObjectAnimatorSweep.setRepeatCount(ValueAnimator.INFINITE);
<span style="white-space:pre">		</span>mObjectAnimatorSweep.addListener(new Animator.AnimatorListener() {
<span style="white-space:pre">			</span>@Override
<span style="white-space:pre">			</span>public void onAnimationStart(Animator animation) {


<span style="white-space:pre">			</span>}


<span style="white-space:pre">			</span>@Override
<span style="white-space:pre">			</span>public void onAnimationEnd(Animator animation) {


<span style="white-space:pre">			</span>}


<span style="white-space:pre">			</span>@Override
<span style="white-space:pre">			</span>public void onAnimationCancel(Animator animation) {


<span style="white-space:pre">			</span>}


<span style="white-space:pre">			</span>@Override
<span style="white-space:pre">			</span>public void onAnimationRepeat(Animator animation) {
<span style="white-space:pre">				</span>toggleAppearingMode();
<span style="white-space:pre">			</span>}
<span style="white-space:pre">		</span>});
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>public void start() {
<span style="white-space:pre">		</span>if (isRunning()) {
<span style="white-space:pre">			</span>return;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>mRunning = true;
<span style="white-space:pre">		</span>// 为了方便测试,可以注释掉下面两个动画中的一个,来
<span style="white-space:pre">		</span>// 分别查看每个独立的动画是如何运动的
<span style="white-space:pre">		</span>mObjectAnimatorAngle.start();
<span style="white-space:pre">		</span>mObjectAnimatorSweep.start();
<span style="white-space:pre">		</span>invalidateSelf();
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>public void stop() {
<span style="white-space:pre">		</span>if (!isRunning()) {
<span style="white-space:pre">			</span>return;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>mRunning = false;
<span style="white-space:pre">		</span>mObjectAnimatorAngle.cancel();
<span style="white-space:pre">		</span>mObjectAnimatorSweep.cancel();
<span style="white-space:pre">		</span>invalidateSelf();
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Override
<span style="white-space:pre">	</span>public boolean isRunning() {
<span style="white-space:pre">		</span>return mRunning;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public void setCurrentGlobalAngle(float currentGlobalAngle) {
<span style="white-space:pre">		</span>mCurrentGlobalAngle = currentGlobalAngle;
<span style="white-space:pre">		</span>invalidateSelf();
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public float getCurrentGlobalAngle() {
<span style="white-space:pre">		</span>return mCurrentGlobalAngle;
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public void setCurrentSweepAngle(float currentSweepAngle) {
<span style="white-space:pre">		</span>mCurrentSweepAngle = currentSweepAngle;
<span style="white-space:pre">		</span>invalidateSelf();
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>public float getCurrentSweepAngle() {
<span style="white-space:pre">		</span>return mCurrentSweepAngle;
<span style="white-space:pre">	</span>}


}


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

/**
 * Simplest custom view possible, using CircularProgressDrawable
 */
public class CustomView extends View {

	private CircularProgressDrawable mDrawable;

	public CustomView(Context context) {
		this(context, null);
	}

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

	public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);

		mDrawable = new CircularProgressDrawable(Color.RED, 10);
		mDrawable.setCallback(this);
	}

	@Override
	protected void onVisibilityChanged(View changedView, int visibility) {
		super.onVisibilityChanged(changedView, visibility);
		if (visibility == VISIBLE) {
			mDrawable.start();
		} else {
			mDrawable.stop();
		}
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mDrawable.setBounds(0, 0, w, h);
	}

	@Override
	public void draw(Canvas canvas) {
		super.draw(canvas);
		mDrawable.draw(canvas);
	}

	@Override
	protected boolean verifyDrawable(Drawable who) {
		return who == mDrawable || super.verifyDrawable(who);
	}
}

抱歉!评论已关闭.