先看看下面這個Gif。看起來還不錯 。
突然想到,前兩天實現的那個波紋,雖然有點Low,確有點異曲同工之感,這個的 一圈圈的波紋 和 變大變小的內圓 其實就是個擺設,既然不能顯示進度,最後的OK感覺有點莫名其妙。
然後進行組合了一下, 大圓代表整個進度,而內心圓代表當前進度。一波波的波紋代表載入效果。完成後顯示OK。
當然Gif圖的 OK 還有一個放大後執行了縮小,又放大了一點點。這一塊,沒有整,累了,睡覺了。。無非設定textSize了。
實現如下:
載入波紋看起來顏色不是挺順眼,奈何老夫不懂配色。
初始化
有三個狀態,預設為None,根據每個狀態提供一個animator。
public class LoadRippleCircleView extends BaseView {
private int mInsideCircleRadius = DisplayUtils.dp2px(this.getContext(), 60);
private int mOuterCircleRadius = mInsideCircleRadius + DisplayUtils.dp2px(this.getContext(), 5);
private Status status = Status.NONE;
private enum Status {
NONE, Starting, Loading, ENDING
}
// 對應三個執行狀態的 value值
private ValueAnimator mStartingValueAnimator, mLoadingValueAnimator, mEndingValueAnimator;
private float mStartingValue = 0.0f, mLoadingValue = 0.0f, mEndingValue = 0.0f;
// 波紋
private int mLoadingRadius = DisplayUtils.dp2px(this.getContext(), 5);
private int mFirstRip = DisplayUtils.dp2px(this.getContext(), 60) / 2;
private int mSecondRip = DisplayUtils.dp2px(this.getContext(), 60) / 4 * 3;
private int mThirdRip = DisplayUtils.dp2px(this.getContext(), 60);
// 將進度內圓進行分份
private int min = 0, max = 100;
private int currValue;
public LoadRippleCircleView(Context context) {
this(context, null);
}
public LoadRippleCircleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public LoadRippleCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mStartingValueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);
mLoadingValueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(700);
mEndingValueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(1000);
initAnimatorListener();
}
private void initAnimatorListener() {
mStartingValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mStartingValue = (float) animation.getAnimatedValue();
postInvalidate();
}
});
mStartingValueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
status = Status.Starting;
}
@Override
public void onAnimationEnd(Animator animation) {
status = Status.Loading;
mLoadingValueAnimator.start();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
mLoadingValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mLoadingValue = (float) animation.getAnimatedValue();
mLoadingRadius = (int) (mInsideCircleRadius * 1.0f / max * currValue);
if (currValue >= max) {
animation.cancel();
status = Status.ENDING;
mEndingValueAnimator.start();
}
postInvalidate();
}
});
mLoadingValueAnimator.setRepeatCount(-1);
mEndingValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mEndingValue = (float) animation.getAnimatedValue();
postInvalidate();
}
});
mStartingValueAnimator.start();
}
複製程式碼
測量大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int measuredWidth = width, measuredHeight = height;
if (widthMode != MeasureSpec.EXACTLY) {
measuredWidth = mOuterCircleRadius * 2 + mMargin * 2 + mPadding * 2;
}
if (heightMode != MeasureSpec.EXACTLY) {
measuredHeight = mOuterCircleRadius * 2 + mMargin * 2 + mPadding * 2;
}
setMeasuredDimension(measuredWidth, measuredHeight);
}
複製程式碼
繪製
starting:開始繪製背景圓。
loading:繪製進度圓,還有波浪。
ending:縮小進度圓後放大字型,放大背景圓(從0開始),同時繪製Path。
(縮小進度圓的速度)比(放大背景圓的速度)快(1/4),剩下的1/4時間放大Ok。
(縮小進度圓 + 放大OK) 與 (背景圓) 與 (Path繪製) 同比。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#FF31C994"));
canvas.translate(mViewWidth / 2, mViewHeight / 2);
canvas.save();
if (status == Status.Starting) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.parseColor("#A1E3CB"));
canvas.drawCircle(0, 0, mInsideCircleRadius * mStartingValue, mPaint);
}
if (status == Status.Loading) {
// 底 圓
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.parseColor("#A1E3CB"));
canvas.drawCircle(0, 0, mInsideCircleRadius, mPaint);
// 波紋
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#ff92C5AA"));
mPaint.setStyle(Paint.Style.STROKE);
if (mFirstRip > mLoadingRadius + 15) // 感覺+15 更為協調些
canvas.drawCircle(0, 0, (mLoadingRadius + (mFirstRip - mLoadingRadius)) * mLoadingValue, mPaint);
if (mSecondRip > mLoadingRadius + 15)
canvas.drawCircle(0, 0, (mLoadingRadius + (mSecondRip - mLoadingRadius)) * mLoadingValue, mPaint);
if (mThirdRip > mLoadingRadius + 15)
canvas.drawCircle(0, 0, (mLoadingRadius + (mThirdRip - mLoadingRadius)) * mLoadingValue, mPaint);
// 進度圓
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.parseColor("#D2F1E6"));
canvas.drawCircle(0, 0, mLoadingRadius, mPaint);
}
if (status == Status.ENDING) {
// 底圓 由小變大
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.parseColor("#A1E3CB"));
canvas.drawCircle(0, 0, mInsideCircleRadius * mEndingValue, mPaint);
if (mEndingValue < 0.75f) {
// 進度圓 由大變小
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.parseColor("#D2F1E6"));
canvas.drawCircle(0, 0, mLoadingRadius * (0.75f - mEndingValue), mPaint);
} else {
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
float f = DisplayUtils.sp2px(this.getContext(), 65) / (1f - 0.75f);
mPaint.setTextSize((mEndingValue - 0.75f) * f);
String txt = "OK";
Rect rect = new Rect();
mPaint.getTextBounds(txt, 0, txt.length(), rect);
int w = rect.width();
int h = rect.height();
canvas.drawText(txt, -(w / 2), (h / 2), mPaint);
}
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(DisplayUtils.dp2px(this.getContext(), 1.5f));
Path pathCircle = new Path();
RectF oval = new RectF(-mOuterCircleRadius, -mOuterCircleRadius, mOuterCircleRadius, mOuterCircleRadius);
pathCircle.addArc(oval, 270, 360f * mEndingValue);
canvas.drawPath(pathCircle, mPaint);
}
canvas.restore();
}
複製程式碼
對外介面
public void setCurrValue(int currValue) {
this.currValue = currValue;
}
public void setMin().....
public void setMax()....
}
複製程式碼