在開發(fā)中經(jīng)常會需要自定義一個加載的動畫。接下來就講下如何自定義view來實現(xiàn)一些簡單有趣的lodingview。
網(wǎng)上優(yōu)秀的列子也很多。下面是我寫的demo,如有錯誤或者問題,請多多包涵和用力的吐糟。
已經(jīng)在github上更新代碼,下面講的只是幾個例子。
下載代碼

效果.png

效果1.gif
1 LVCircularCD
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStrokeWidth(2);
canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2 - mPadding, mPaint);//畫背景
mPaint.setStrokeWidth(3);
canvas.drawCircle(mWidth / 2, mWidth / 2, mPadding, mPaint);//畫中心圓
mPaint.setStrokeWidth(2);
// 下面是畫弧線
RectF rectF = new RectF(mWidth / 2 - mWidth / 3, mWidth / 2 - mWidth / 3,
mWidth / 2 + mWidth / 3, mWidth / 2 + mWidth / 3);
canvas.drawArc(rectF, 0,80
, false, mPaint);
canvas.drawArc(rectF, 180,80
, false, mPaint);
RectF rectF2 = new RectF(mWidth / 2 - mWidth / 4, mWidth / 2 - mWidth / 4,
mWidth / 2 + mWidth / 4, mWidth / 2 + mWidth / 4);
canvas.drawArc(rectF2, 0, 80
, false, mPaint);
canvas.drawArc(rectF2, 180, 80
, false, mPaint);
}
//不停旋轉(zhuǎn)的動畫初始化
RotateAnimation mProgerssRotateAnim = new RotateAnimation(0f, 360f, android.view.animation.Animation.RELATIVE_TO_SELF,
0.5f, android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f);
mProgerssRotateAnim.setRepeatCount(-1);
mProgerssRotateAnim.setInterpolator(new LinearInterpolator());//不停頓
mProgerssRotateAnim.setFillAfter(true);//停在最后
2 LVCircularRing 主要是控制弧線的開始角度,動畫中去控制startAngle = 360 * value;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.argb(100, 255, 255, 255));
canvas.drawCircle(mWidth / 2, mWidth / 2, mWidth / 2 - mPadding, mPaint);
mPaint.setColor(Color.WHITE);
RectF rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding);
canvas.drawArc(rectF, startAngle, 100
, false, mPaint);//第三個參數(shù)是否顯示半徑
}
ValueAnimator valueAnimator;
private ValueAnimator startViewAnim(float startF, final float endF, long time) {
valueAnimator = ValueAnimator.ofFloat(startF, endF);
valueAnimator.setDuration(time);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);//無限循環(huán)
valueAnimator.setRepeatMode(ValueAnimator.RESTART);//
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float) valueAnimator.getAnimatedValue();
startAngle = 360 * value;
invalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
if (!valueAnimator.isRunning()) {
valueAnimator.start();
}
return valueAnimator;
}
3 LVCircular 類似1的做法,看代碼就明白
4 LVCircularSmile 主要是在動畫進行一半后顯示笑臉。mAnimatedValue < 0.5不顯示,反之笑臉
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rectF = new RectF(mPadding, mPadding, mWidth - mPadding, mWidth - mPadding);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(rectF, startAngle, 180
, false, mPaint);//第三個參數(shù)是否顯示半徑
mPaint.setStyle(Paint.Style.FILL);
if (isSmile) {
canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
}
}
float mAnimatedValue = 0f;
private ValueAnimator startViewAnim(float startF, final float endF, long time) {
valueAnimator = ValueAnimator.ofFloat(startF, endF);
valueAnimator.setDuration(time);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);//無限循環(huán)
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatedValue = (float) valueAnimator.getAnimatedValue();
if (mAnimatedValue < 0.5) {
isSmile = false;
startAngle = 720 * mAnimatedValue;
} else {
startAngle = 720;
isSmile = true;
}
invalidate();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
}
});
if (!valueAnimator.isRunning()) {
valueAnimator.start();
}
return valueAnimator;
}
5 LVCircularJump,主要控制第幾個圓圈起跳,在一個動畫結(jié)束后下一個開始。
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
mJumpValue++;
}
});
6 LVCircularZoom 和5思路一致
7 LVPlayBall 利用貝塞爾曲線畫出彈動的效果
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Path path = new Path();
path.moveTo(0 + mRadius * 2 + mPaintStrokeWidth, getMeasuredHeight() / 2);
path.quadTo(mWidth / 2, quadToStart, mWidth - mRadius * 2 - mPaintStrokeWidth, mHigh / 2);
mPaint.setStrokeWidth(2);
canvas.drawPath(path, mPaint);
mPaintCircle.setStrokeWidth(mPaintStrokeWidth);
canvas.drawCircle(mRadius + mPaintStrokeWidth, mHigh / 2, mRadius, mPaintCircle);
canvas.drawCircle(mWidth - mRadius - mPaintStrokeWidth, mHigh / 2, mRadius, mPaintCircle);
if (ballY - mRadiusBall > mRadiusBall) {
canvas.drawCircle(mWidth / 2, ballY - mRadiusBall, mRadiusBall, mPaintBall);
} else {
canvas.drawCircle(mWidth / 2, mRadiusBall, mRadiusBall, mPaintBall);
}
}
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float value = (float) valueAnimator.getAnimatedValue();
if (value > 0.75) {//這里控制反彈的效果
quadToStart = mHigh / 2 - (1f - (float) valueAnimator.getAnimatedValue()) * mHigh / 3f;
} else {
quadToStart = mHigh / 2 + (1f - (float) valueAnimator.getAnimatedValue()) * mHigh / 3f;
}
if (value > 0.35f) {
ballY = mHigh / 2 - (mHigh / 2 * value);
} else {
ballY = mHigh / 2 + (mHigh / 6 * value);
}
invalidate();
}
});
8 LVLineWithText
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String text = mVlaue + "%";
float textlength = getFontlength(mPaint, text);
float texthigh = getFontHeight(mPaint, text);
if (mVlaue == 0) {
canvas.drawText(text, mPadding, mHigh / 2 + texthigh / 2, mPaint);
canvas.drawLine(mPadding + textlength, mHigh / 2, mWidth - mPadding, mHigh / 2, mPaint);
} else if (mVlaue >= 100) {
canvas.drawText(text, mWidth - mPadding - textlength, mHigh / 2 + texthigh / 2, mPaint);
canvas.drawLine(mPadding, mHigh / 2, mWidth - mPadding - textlength, mHigh / 2, mPaint);
} else {
float w = mWidth - 2 * mPadding - textlength;
canvas.drawLine(mPadding, mHigh / 2, mPadding + w * mVlaue / 100, mHigh / 2, mPaint);
canvas.drawLine(mPadding + w * mVlaue / 100 + textlength, mHigh / 2, mWidth - mPadding, mHigh / 2, mPaint);
canvas.drawText(text, mPadding + w * mVlaue / 100, mHigh / 2 + texthigh / 2, mPaint);
}
}
9 LVEatBeans 簡單的吃豆子動畫
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float eatRightX = mPadding + eatErWidth + eatErPositonX;
RectF rectF = new RectF(mPadding + eatErPositonX, mHigh / 2 - eatErWidth / 2, eatRightX, mHigh / 2 + eatErWidth / 2);
canvas.drawArc(rectF, eatErStrtAngle, eatErEndAngle
, true, mPaint);//畫頭像
canvas.drawCircle(mPadding + eatErPositonX + eatErWidth / 2,
mHigh / 2 - eatErWidth / 4,
beansWidth / 2, mPaintEye);//畫眼睛
int beansCount = (int) ((mWidth - mPadding * 2 - eatErWidth) / beansWidth / 2);
//畫豆子
for (int i = 0; i < beansCount; i++) {
float x = beansCount * i + beansWidth / 2 + mPadding + eatErWidth;
if (x > eatRightX) {
canvas.drawCircle(x,
mHigh / 2, beansWidth / 2, mPaint);
}
}
}
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float mAnimatedValue = (float) valueAnimator.getAnimatedValue();
eatErPositonX = (mWidth - 2 * mPadding - eatErWidth) * mAnimatedValue;
//一次動畫進行四次嘴巴張開關(guān)閉的效果
eatErStrtAngle = mAngle * (1 - (mAnimatedValue * eatSpeed - (int) (mAnimatedValue * eatSpeed)));
eatErEndAngle = 360 - eatErStrtAngle * 2;
invalidate();
}
});