那1刻 我升起風馬 不為乞福 只為守候你的到來
那1天 閉目在經殿香霧中 驀然聽見 你頌經中的真言
那1日 壘起瑪尼堆 不為修德 只為投下心湖的石子
那1夜 我聽了1宿梵唱 不為參悟 只為尋你的1絲氣味
有時候我會為了實現某個動畫的算法而挖空心思,苦苦思考。常常會在草稿上面繪制動畫軌跡,真的很多時候幾天就在糾結1個動畫的實現,不過最后成功后真的很滿足喜悅。
我陸續出過1些LodingView
的文章,但都比較雜亂。近來我在網上看到1張圖,并通過代碼實現了出來。在這邊非常感謝@ldoublem
,我會不斷更新LodingView
把好看及簡潔的動畫集成進來,希望能夠幫助到你。
下載源碼
先出圖說話:
public class WSCircleCD extends View {
private Paint mPaint;
private Context mContext;
private int circleCenterX, circleCenterY;
private int circleRadius;
private final static float RADIUS_RATIO = 2 / 3f;
public WSCircleCD(Context context) {
this(context, null);
}
public WSCircleCD(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WSCircleCD(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mContext = context;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
//處理 wrap_content問題
int defaultDimension = dip2px(100);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(defaultDimension, defaultDimension);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(defaultDimension, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, defaultDimension);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
circleCenterX = w / 2;
circleCenterY = h / 2;
//處理padding情況
circleRadius = (int) (Math.min(Math.min(circleCenterY - getPaddingTop(), circleCenterY - getPaddingBottom()),
Math.min(circleCenterX - getPaddingLeft(), circleCenterX - getPaddingRight())) * RADIUS_RATIO);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(dip2px(2));
canvas.drawCircle(circleCenterX, circleCenterY, circleRadius, mPaint);//畫大圓
canvas.drawCircle(circleCenterX, circleCenterY, dip2px(4), mPaint);//畫中心小圓圓
// 下面是畫弧線
RectF rectF = new RectF(circleCenterX - circleRadius * RADIUS_RATIO, circleCenterY - circleRadius * RADIUS_RATIO,
circleCenterX + circleRadius * RADIUS_RATIO, circleCenterY + circleRadius * RADIUS_RATIO);
canvas.drawArc(rectF, 0, 80, false, mPaint);
canvas.drawArc(rectF, 180, 80, false, mPaint);
rectF = new RectF(circleCenterX - circleRadius / 2, circleCenterY - circleRadius / 2,
circleCenterX + circleRadius / 2, circleCenterY + circleRadius / 2);
canvas.drawArc(rectF, 0, 80, false, mPaint);
canvas.drawArc(rectF, 180, 80, false, mPaint);
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
RotateAnimation animation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setInterpolator(new LinearInterpolator());
animation.setRepeatCount(-1);
animation.setDuration(1000);
animation.setFillAfter(true);
startAnimation(animation);
}
});
}
public void setPaintColor(int color) {
mPaint.setColor(color);
}
/**
* 根據手機的分辨率從 dp 的單位 轉成為 px(像素)
*/
public int dip2px(float dpValue) {
final float scale = mContext.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
我主要講授下紅字注釋的地方,為何自定義1個View在布局時將其大小設為wrap_content但其實際卻是match_parent的效果?請查閱谷歌的小弟1自定義View系列教程02–onMeasure源碼詳實分析;處理padding
情況,不然布局xml
文件設置padding
值將無效;繪制進程比較簡單,我這里就不再詳細講授,有不懂請給我留言。
WSCircleSun主要是控制小圓的旋轉,每次移動角度就增加moveAngle
。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(circleRadius * SMALL_RADIUS_RATIO);
//繪制小圓
for (int i = 0; i < SMALL_CIRCLE_COUNT; i++) {
canvas.drawCircle((float) (circleCenterX + circleRadius * Math.sin(Math.toRadians(i * 360 / SMALL_CIRCLE_COUNT + moveAngle))),
(float) (circleCenterY + circleRadius * Math.cos(Math.toRadians(i * 360 / SMALL_CIRCLE_COUNT + moveAngle))),
circleRadius * SMALL_RADIUS_RATIO,
mPaint);
}
//繪制大圓
canvas.drawCircle(circleCenterX, circleCenterY, circleRadius * LARGE_RADIUS_RATIO, mPaint);
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofInt(0, 361);
animator.setDuration(3000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
moveAngle = (int) valueAnimator.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
});
}
WSCircleRing
主要是控制弧度的startAngle
。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(circleRadius * RING_RADIUS_RATIO);
canvas.drawCircle(circleCenterX, circleCenterY, circleRadius, mPaint);//畫背景
mPaint.setColor(Color.parseColor("#FF4081"));
RectF rectF = new RectF(circleCenterX - circleRadius, circleCenterY - circleRadius,
circleCenterX + circleRadius, circleCenterY + circleRadius);
canvas.drawArc(rectF, 0 + moveAngle, 80, false, mPaint);
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofInt(0, 361);
animator.setDuration(1000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
moveAngle = (int) valueAnimator.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
});
}
WSCircleFace主要是控制臉的出現和消失,動畫出現1半后臉出現,反之消失。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(circleRadius * FACE_RADIUS_RATIO);
RectF rectF = new RectF(circleCenterX - circleRadius, circleCenterY - circleRadius,
circleCenterX + circleRadius, circleCenterY + circleRadius);
canvas.drawArc(rectF, startAngle, 180, false, mPaint);
mPaint.setStyle(Paint.Style.FILL);
if (isFace) { //畫臉
canvas.drawCircle((float) (circleCenterX - circleRadius * Math.sin(Math.toRadians(EYE_ROUND))),
(float) (circleCenterY - circleRadius * Math.cos(Math.toRadians(EYE_ROUND))), mPaint.getStrokeWidth() * 3 / 2, mPaint);
canvas.drawCircle((float) (circleCenterX + circleRadius * Math.sin(Math.toRadians(EYE_ROUND))),
(float) (circleCenterY - circleRadius * Math.cos(Math.toRadians(EYE_ROUND))), mPaint.getStrokeWidth() * 3 / 2, mPaint);
}
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setDuration(1500);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float mAnimatedValue = (float) valueAnimator.getAnimatedValue();
if (mAnimatedValue < 0.5) {
isFace = false;
startAngle = (int) (720 * mAnimatedValue);
} else {
startAngle = 720;
isFace = true;
}
postInvalidate();
}
});
animator.start();
}
});
}
WSCircleJump主要是控制小圓的彈跳,上1個小圓彈跳結束后下1個小圓開始。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(mRadius * JUMP_BALL_RATIO);
//畫小球
for (int i = 0; i < BALL_COUNT; i++) {
if (i == currentBallPosition) {
canvas.drawCircle((centerX - mRadius) + 2 * mRadius / (BALL_COUNT - 1) * i,
centerY - ballJumpY, mPaint.getStrokeWidth(), mPaint);
} else {
canvas.drawCircle((centerX - mRadius) + 2 * mRadius / (BALL_COUNT - 1) * i,
centerY, mPaint.getStrokeWidth(), mPaint);
}
}
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setDuration(500);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatedValue = (float) valueAnimator.getAnimatedValue();
if (mAnimatedValue < 0.5) {
ballJumpY = mAnimatedValue * mRadius;
} else {
ballJumpY = (1 - mAnimatedValue) * mRadius;
}
postInvalidate();
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
currentBallPosition++;
if (currentBallPosition >= BALL_COUNT) {
currentBallPosition = 0;
}
}
});
animator.start();
}
});
}
WSGears主要是繪制大小齒輪。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(outCircleRadius * GEAR_RADIUS_RATIO);
//60 是偏移量 繪制直線
for (int i = 0; i < 360 / 120; i++) {
canvas.drawLine(centerX, centerY, (float) (centerX + outCircleRadius * Math.sin(Math.toRadians(i * 120 + 60))),
(float) (centerY + outCircleRadius * Math.cos(Math.toRadians(i * 120 + 60))), mPaint);
}
//繪制外圓齒輪
for (int i = 0; i < 360 / 8; i++) {
canvas.drawLine((float) (centerX + outCircleRadius * Math.sin(Math.toRadians(i * 8 + moveAngle))),
(float) (centerY + outCircleRadius * Math.cos(Math.toRadians(i * 8 + moveAngle))),
(float) (centerX + (outCircleRadius + dip2px(4)) * Math.sin(Math.toRadians(i * 8 + moveAngle))),
(float) (centerY + (outCircleRadius + dip2px(4)) * Math.cos(Math.toRadians(i * 8 + moveAngle))), mPaint);
}
//繪制內圓齒輪
mPaint.setStrokeWidth(inCircleRadius * GEAR_RADIUS_RATIO);
for (int i = 0; i < 360 / 8; i++) {
canvas.drawLine((float) (centerX + inCircleRadius * Math.sin(Math.toRadians(i * 8 - moveAngle))),
(float) (centerY + inCircleRadius * Math.cos(Math.toRadians(i * 8 - moveAngle))),
(float) (centerX + (inCircleRadius + dip2px(4)) * Math.sin(Math.toRadians(i * 8 - moveAngle))),
(float) (centerY + (inCircleRadius + dip2px(4)) * Math.cos(Math.toRadians(i * 8 - moveAngle))), mPaint);
}
mPaint.setStrokeWidth(mPaint.getStrokeWidth() * 2);
canvas.drawCircle(centerX, centerY, outCircleRadius, mPaint);
canvas.drawCircle(centerX, centerY, inCircleRadius, mPaint);
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setDuration(5000);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatedValue = (float) valueAnimator.getAnimatedValue();
moveAngle = (int) (mAnimatedValue * 360);
postInvalidate();
}
});
animator.start();
}
});
}
WSJump利用貝塞爾曲線實現彈動效果。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(dip2px(2));
//繪制貝塞爾曲線
Path mPath = new Path();
mPath.moveTo(centerX - mRadius, centerY);
mPath.quadTo(centerX, centerY + quadMoveY, centerX + mRadius, centerY);
canvas.drawPath(mPath, mPaint);
//繪制2邊小球 dip2px(4)為小球半徑 分別加上和減去半徑
canvas.drawCircle(centerX - mRadius - dip2px(4), centerY, dip2px(4), mPaint);
canvas.drawCircle(centerX + mRadius + dip2px(4), centerY, dip2px(4), mPaint);
//繪制中間小球 dip2px(4+3) 兩邊小球的半徑 加上自己的半徑
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(centerX, centerY - dip2px(4 + 3) - mJumpY, dip2px(6), mPaint);
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setDuration(500);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float) valueAnimator.getAnimatedValue();
if (value > 0.75f) { // 0.75 =0.25*3
quadMoveY = mRadius * (1 - value) * 3;
} else {
quadMoveY = value * mRadius;
}
if (value > 0.35f) {// 0.7 =0.35*2
mJumpY = (1 - value) * mRadius;
} else {
mJumpY = value * mRadius * 2;
}
postInvalidate();
}
});
animator.start();
}
});
}
WSFiveStar主要控制線條的繪制,1條直線從出發點繪制到終點,終點作為下1條直線的出發點,順次繪制。利用mPath.rLineTo
增量的方式繪制直線,如果你敢興趣可以通過設置setRegularPolygon(int regularPolygon)
方法設置需要繪制的是正多邊形,我繪制出過正2104邊形。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(dip2px(2));
lineStartX = (float) (circleCenterX + circleRadius * Math.cos(Math.toRadians(startAngle)));
lineStartY = (float) (circleCenterY + circleRadius * Math.sin(Math.toRadians(startAngle)));
mPath.moveTo(lineStartX, lineStartY);
lineEndX = (float) (circleCenterX + circleRadius * Math.cos(Math.toRadians(startAngle + polygonAngle * 2)));
lineEndY = (float) (circleCenterY + circleRadius * Math.sin(Math.toRadians(startAngle + polygonAngle * 2)));
mPath.rLineTo((lineEndX - lineStartX) * mValue, (lineEndY - lineStartY) * mValue);
canvas.drawPath(mPath, mPaint);
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setDuration(500);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mValue = (float) animation.getAnimatedValue();
if (mValue >= 0.9f) {
mValue = 1.0f;
}
postInvalidate();
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
startAngle += polygonAngle * 2;
if (startAngle > polygonAngle * 2 * regularPolygon) {
startAngle = 0;
mPath.reset();
}
}
});
animator.start();
}
});
}
WSLineProgress主要是控制兩段直線的繪制。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(dip2px(2));
mPaint.setTextSize(dip2px(20));
text = mValue + "%";
fontWidth = mPaint.measureText(text);
fontHeight = getFontHeight(mPaint, text);
if (mValue == 0) {
canvas.drawText(text, centerX - mRadius, centerY + fontHeight / 2, mPaint);
canvas.drawLine(centerX - mRadius + fontWidth, centerY, centerX + mRadius, centerY, mPaint);
} else if (mValue >= 100) {
canvas.drawText(text, centerX + mRadius - fontWidth, centerY + fontHeight / 2, mPaint);
canvas.drawLine(centerX - mRadius, centerY, centerX + mRadius - fontWidth, centerY, mPaint);
} else {
float lineWidth = 2 * mRadius - fontWidth;
//左側直線
canvas.drawLine(centerX - mRadius, centerY, centerX - mRadius + (float) mValue / 100 * lineWidth, centerY, mPaint);
//右側直線
canvas.drawLine(centerX - mRadius + (float) mValue / 100 * lineWidth + fontWidth, centerY, centerX + mRadius, centerY, mPaint);
//繪制文本
canvas.drawText(text, centerX - mRadius + (float) mValue / 100 * lineWidth, centerY + fontHeight / 2, mPaint);
}
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofInt(0, 101);
animator.setDuration(5000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mValue = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
});
}
WSEatBeans主要是控制嘴的張合,豆數量的變化。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(dip2px(2));
RectF rectF = new RectF(centerX - mRadius + moveX, centerY - eatRadius, centerX - mRadius + eatRadius * 2
+ moveX, centerY + eatRadius);
canvas.drawArc(rectF, eatStartAngle, eatSweepAngle, true, mPaint);
//畫眼睛
mPaint.setColor(Color.BLACK);
canvas.drawCircle(centerX - mRadius + eatRadius + moveX, centerY - eatRadius / 2, dip2px(2), mPaint);
//繪制豆
mPaint.setColor(Color.WHITE);
//豆的個數是間隔數減去1
int count = (2 * (mRadius - eatRadius)) / eatRadius - 1;
for (int i = eatBeans; i < count; i++) {
canvas.drawCircle(centerX - mRadius + eatRadius * 2 +
eatRadius * (i + 1), centerY, dip2px(2), mPaint);
}
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setDuration(3000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
moveX = (int) ((float) animation.getAnimatedValue() * 2 * (mRadius - eatRadius));
eatBeans = moveX / eatRadius;
if (eatState == 0) {
eatStartAngle -= 2;
if (eatStartAngle <= 0) {
eatState = 1;
}
} else if (eatState == 1) {
eatStartAngle += 2;
if (eatStartAngle >= 30) {
eatState = 0;
}
}
eatSweepAngle = 360 - eatStartAngle * 2;
postInvalidate();
}
});
animator.start();
}
});
}
WSCircleBar,WSCircleArc,WSCircleRise類似。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(circleRadius * BAR_RADIUS_RATIO);
mPaint.setColor(Color.WHITE);
//畫背景
canvas.drawCircle(circleCenterX, circleCenterY, circleRadius, mPaint);
//畫bar
RectF rect = new RectF(circleCenterX - circleRadius, circleCenterY - circleRadius,
circleCenterX + circleRadius, circleCenterY + circleRadius);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mPaint.getStrokeWidth() * 2);
canvas.drawArc(rect, startAngle, sweepAngle, false, mPaint);
//畫文字
mPaint.setStyle(Paint.Style.FILL);
text = (int) (mValueAnimator * 100) + "%";
mPaint.setTextSize(mPaint.getStrokeWidth() * 2);
canvas.drawText(text, circleCenterX - mPaint.measureText(text) / 2, circleCenterY + getFontHeight(mPaint, text) / 2, mPaint);
}
//開始動畫
public void startAnimator() {
post(new Runnable() {
@Override
public void run() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
animator.setDuration(5000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatMode(ValueAnimator.RESTART);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mValueAnimator = (float) valueAnimator.getAnimatedValue();
sweepAngle = 360 * mValueAnimator;
postInvalidate();
}
});
animator.start();
}
});
}
在此非常感謝ldoublem 的效果圖并通過代碼實現了出來,我們實現進程有很多不同的地方。
LoadingView
我會不斷更新,敬請你的期待。
下載源碼
上一篇 Android中的自定義注解
下一篇 算法精解:最小二乘法C實現