Gif(2)-載入檢視-波紋

木漿果發表於2018-04-30

先看看下面這個Gif。看起來還不錯 。

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()....
}
複製程式碼

src

相關文章