美文网首页AndroidAndroid开发Android开发经验谈
Android动画篇(四):最终效果篇CircleProgres

Android动画篇(四):最终效果篇CircleProgres

作者: 珠穆朗玛小王子 | 来源:发表于2017-09-13 10:33 被阅读69次

    前言#

    今天终于有时间把最后的成果分享给大家了,为了提高一下博客的逼格,我也找了一个专门做原型、导图的在线网站:processon(www.processon.com,这个工具真的很棒,也很方便,这里给他点个赞。

    CircleProgressSuperBar为了完成最终的效果,我也是踩了一些坑,今天把我总结的最清晰的思路分享给大家,首先我们回顾一下我们的效果图:

    这里写图片描述

    正文#

    首先我们来分析一下,CircleProgressSuperBar总共分为几种状态:

    这里写图片描述

    这四种状态,我们不关心是怎么切换的,我们只关心动画是怎么过渡的。

    首先我们知道准备一些类:

    1、CircleProgressSuperBar,这个是主类,也是最终要完成的view。
    2、四个状态的Drawable,我们分别命名为:NormalDrawable、LoadingDrawable、CompleteDrawable和ErrorDrawable。

    最终为了方便扩展和解耦,我最终实现的架构图是这样的:

    这里写图片描述

    我的主要目的:

    1、在View和Drawable之间创建工厂类,即能降低类之间的耦合,也可以方便扩展更多状态的Drawable。

    2、各种状态Drawble的基类BaseStatusDrawable,封装公共的属性和方法,让View直接使用BaseStatusDrawable类型,而不去具体关心具体的Drawable的实现。

    3、BaseStatusDrawable内部带有样式的信息,防止和内部的画笔有关的颜色弄混。

    那我们就从最基础的部分,首先新建CircleProgressSuperInfo:

    public class CircleProgressSuperInfo {
    
        /**
         * 宽, 在设置动画的时候需要知道宽
         */
        private int mWidth;
    
        /**
         * 高,在设置动画的时候需要知道高
         */
        private int mHeight;
    
        /**
         * 圆角
         */
        private int mRadius;
    
        /**
         * 背景颜色
         */
        private int mBgColor;
    
        /**
         * 边框颜色
         */
        private int mBorderColor;
    
        /**
         * 边框的宽度
         */
        private int mBorderWidth;
    
        /**
         * 最大的间距
         * */
        private float mPadding;
    
        public CircleProgressSuperInfo(int bgColor, int borderColor, int borderWidth) {
            this.mBgColor = bgColor;
            this.mBorderColor = borderColor;
            this.mBorderWidth = borderWidth;
        }
    
        ... 
        // 此处省略setter和getter方法
    }
    

    然后就是需要BaseStatusDrawable,我们直接把之前写好的形状变化的ChangeShapeAndColorButton进行改造,变成我们需要的BaseStatusDrawable:

    /**
     * Created by li.zhipeng on 2017/7/12.
     * <p>
     * 所有的状态图片需要实现此接口
     */
    public abstract class BaseStatusDrawable extends Drawable {
    
        protected CircleProgressSuperInfo mInfo;
    
        /**
         * 宽, 在设置动画的时候需要知道宽
         */
        protected int mWidth;
    
        /**
         * 高,在设置动画的时候需要知道高
         */
        protected int mHeight;
    
        /**
         * 圆角
         */
        protected float mRadius;
    
        /**
         * 文字
         */
        protected String mText;
    
        /**
         * 文字颜色
         */
        protected int mTextColor = Color.parseColor("#ffffff");
    
        /**
         * 文字大小
         */
        protected int mTextSize;
    
        /**
         * 画笔
         */
        protected Paint mPaint;
    
        /**
         * 形状
         */
        protected RectF mRectF;
    
    
        /**
         * 背景颜色
         */
        protected int mBgColor;
    
        /**
         * 边框颜色
         */
        protected int mBorderColor;
    
        /**
         * 边框的宽度
         */
        protected int mBorderWidth;
    
        /**
         * 偏移值,也就是大小要发生的变化值
         */
        protected float mPadding;
    
        /**
         * 最小大小
         */
        protected float mMinSize = -1;
    
        /**
         * 正在动画在中
         */
        protected boolean isAnim;
    
        public BaseStatusDrawable(CircleProgressSuperInfo info) {
            this.mInfo = info;
            // 初始化画笔
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setDither(true);
            mRectF = new RectF();
        }
        
        // 此处省略各种setter和getter方法
        ...
    
        public CircleProgressSuperInfo getInfo(){return this.mInfo;};
    
        /**
         * 回到后台,调用此方法
         */
        public abstract void release();
    
        /**
         * 绘制
         */
        @Override
        public void draw(@NonNull Canvas canvas) {
            // 这是绘制过渡动画
            if (isAnim()) {
                drawTransition(canvas);
            }
            // 绘制正常状态,例如loading就要使用绘制旋转的圆圈
            else {
                drawSelf(canvas);
            }
        }
    
        /**
         * 绘制过度动画
         */
        protected void drawTransition(Canvas canvas) {
            // 先画出背景,背景是居中的
            // 判断宽高
            int width = getWidth();
            int height = getHeight();
    
            // 计算左右的间距值,并且判断不能小于minSize
            float paddingLR = width - mPadding * 2 < mMinSize ? (width - mMinSize) / 2 : mPadding;
            float paddingTB = height - mPadding * 2 < mMinSize ? (height - mMinSize) / 2 : mPadding;
    
            // 绘制描边
            mRectF.set(paddingLR + mBorderWidth / 2, paddingTB + mBorderWidth / 2, getWidth() - paddingLR - mBorderWidth / 2,
                    getHeight() - paddingTB - mBorderWidth / 2);
    
            // 开始画后面的背景
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(mBgColor);
            canvas.drawRoundRect(mRectF, mRadius, mRadius, mPaint);
    
    
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(mBorderColor);
            mPaint.setStrokeWidth(mBorderWidth);
            canvas.drawRoundRect(mRectF, mRadius, mRadius, mPaint);
    
            // 居中绘制文字
            if (!TextUtils.isEmpty(mText)) {
                float textDescent = mPaint.getFontMetrics().descent;
                float textAscent = mPaint.getFontMetrics().ascent;
                float delta = Math.abs(textAscent) - textDescent;
                mPaint.setColor(mTextColor);
                mPaint.setTextSize(mTextSize);
                float textWidth = mPaint.measureText(mText);
                canvas.drawText(mText, (width - textWidth) / 2, height / 2 + delta / 2, mPaint);
            }
        }
    
    
        /**
         * 绘制正常状态
         */
        public abstract void drawSelf(Canvas canvas);
    
    
        @Override
        public void setAlpha(@IntRange(from = 0, to = 255) int i) {
    
        }
    
        @Override
        public void setColorFilter(@Nullable ColorFilter colorFilter) {
        }
    
        @Override
        public int getOpacity() {
            return PixelFormat.TRANSLUCENT;
        }
    }
    

    这里要强调几点:

    1、继承Drawable的原因:主要是为了重绘,例如之后的加载状态,是需要不断重绘的才会有转圈的动画,但是已经和View分离了,就无法借助View.invaliate(),所以这里继承了Drawable。

    2、drawSelf()是绘制非过渡动画的状态,这里主要是给LoadingDrawable用的。

    3、getOpacity()方法的作用:返回图片的质量类型。

    为了理解getOpacity的作用,我们就先看一下他的源码和注释:

    // 截取的注释,getOpacity值能返回一下几个值
    PixelFormat.UNKNOWN
    PixelFormat.TRANSLUCENT
    PixelFormat.TRANSPARENT
    PixelFormat.OPAQUE
    
    // 系统自动适配
    public static final int UNKNOWN     = 0;
    
    // 简单的说就是支持半透明
    public static final int TRANSLUCENT = -3;
    
    // 支持全透明
    public static final int TRANSPARENT = -2;
        
    // 不支持透明
    public static final int OPAQUE      = -1;
    
    

    属性的命名规则就是见字如见人,字面意思就是这样。其他两个方法,这里没有用到就不说明了,有兴趣的可以自己去研究研究。

    这个时候去就可以创建四个状态的Drawable了,普通状态、完成状态还有错误状态都是一样的,所以就只贴一个类的代码了:

    /**
     * Created by li.zhipeng on 2017/7/12.
     * <p>
     * 正常状态下的图片
     */
    
    public class NormalDrawable extends BaseStatusDrawable {
    
        public NormalDrawable(CircleProgressSuperInfo info) {
            super(info);
            // 普通状态的颜色要设置,其他的不用设置
            setBgColor(info.getBgColor());
            setBorderColor(info.getBorderColor());
        }
    
        @Override
        public void drawSelf(Canvas canvas) {
            drawTransition(canvas);
        }
    
        @Override
        public void release() {
    
        }
        
    }
    

    重点是LoadingDrawable,其实也很简单,因为继承的关系,现在只需要关心绘制加载状态就足够了,这个时候把我们第一篇CircleProgressBar进行改造:

    /**
     * Created by li.zhipeng on 2017/7/12.
     * <p>
     * 加载状态或是进度条的drawable
     */
    
    public class LoadingDrawable extends BaseStatusDrawable {
    
        /**
         * 圆周的角度
         */
        private static final Float CIRCULAR = 360f;
    
        /**
         * 进度
         */
        private float mProgress = 50;
    
        /**
         * 最大进度
         */
        private int mMaxProgress = 100;
    
        /**
         * 边框颜色,也就是进度的颜色
         */
        private int mProgressColor = Color.parseColor("#ff00ff");
    
        /**
         * 绘制的不全进度的颜色
         */
        private int mDrawBorderColor;
    
        /**
         * 要绘制的进度条的颜色
         */
        private int mDrawProgressColor;
    
        /**
         * 是否打开过度模式,也就是我们平时看到的类似追赶的效果
         */
        private boolean mIsIntermediateMode = true;
    
        /**
         * 最小弧度,进度条过度模式最小的弧度
         */
        private int mMinProgress = 5;
    
        /**
         * 过度动画的时间
         */
        private static final int DURATION = 1000;
    
        /**
         * 过度动画
         */
        private ValueAnimator valueAnimator;
    
        /**
         * 开始角度,在过度动画中使用
         */
        private float mStartAngle = -90f;
    
        public LoadingDrawable(CircleProgressSuperInfo info) {
            super(info);
        }
    
        /**
         * 设置进度
         */
        public void setProgress(float progress) {
            this.mProgress = progress;
        }
    
        /**
         * 获取进度条的颜色
         */
        public int getProgressColor() {
            return this.mProgressColor;
        }
    
        /**
         * 设置进度条的颜色
         */
        public void setProgressColor(int color) {
            this.mProgressColor = color;
        }
    
        /**
         * 设置进度条的颜色
         */
        public void setDrawProgressColor(int color) {
            this.mDrawProgressColor = color;
        }
    
        public int getDrawProgressColor() {
            return mDrawProgressColor;
        }
    
        public int getDrawBorderColor() {
            return mDrawBorderColor;
        }
    
        public void setDrawBorderColor(int mDrawBorderColor) {
            this.mDrawBorderColor = mDrawBorderColor;
        }
    
        /**
         * 是否是过度模式
         */
        public boolean isIntermediateMode() {
            return mIsIntermediateMode;
        }
    
        /**
         * 设置绘制区域
         */
        @Override
        public void setRadius(float radius) {
            super.setRadius(radius);
            // 计算要绘制的区域
            mRectF.set(mWidth / 2 - mRadius + mBorderWidth / 2, mHeight / 2 - mRadius + mBorderWidth / 2,
                    mWidth / 2 + mRadius - mBorderWidth / 2, mHeight / 2 + mRadius - mBorderWidth / 2);
        }
    
        /**
         * 设置loading模式
         */
        public void setIntermediateMode(boolean intermediateMode) {
            if (mIsIntermediateMode != intermediateMode) {
                this.mIsIntermediateMode = intermediateMode;
                // 取消动画
                if (!mIsIntermediateMode) {
                    valueAnimator.cancel();
                } else {
                    //这里要开启动画
                    startIntermediateAnim();
                }
            }
        }
    
        @Override
        public void drawSelf(Canvas canvas) {
            // 是否要显示loading状态
            if (mIsIntermediateMode) {
                startIntermediateAnim();
                drawIntermediateProgress(canvas);
            }
            // 绘制进度条
            else {
                drawProgress(canvas);
            }
        }
    
        /**
         * 绘制过度进度条
         */
        private void drawIntermediateProgress(Canvas canvas) {
            // 首先画出背景圆
            mPaint.setColor(mBgColor);
            mPaint.setStyle(Paint.Style.FILL);
            // 这里减去了边框的宽度
            canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius - mBorderWidth, mPaint);
            // 画出进度条
            mPaint.setColor(mDrawProgressColor);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(mBorderWidth);
            // 计算圆弧划过的角度
            float angle = CIRCULAR / mMaxProgress * mProgress;
            // 这里要画圆弧
            canvas.drawArc(mRectF, mStartAngle, angle, false, mPaint);
    
            // 画出另一部分的进度条
            mPaint.setColor(mDrawBorderColor);
            mPaint.setStrokeWidth(mBorderWidth);
            // 这里要画圆弧
            canvas.drawArc(mRectF, mStartAngle + angle, CIRCULAR - angle, false, mPaint);
        }
    
        /**
         * 绘制进度条
         */
        private void drawProgress(Canvas canvas) {
            // 开始画进度条
            // 首先画出背景圆
            mPaint.setColor(mBgColor);
            mPaint.setStyle(Paint.Style.FILL);
            // 这里减去了边框的宽度
            canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius - mBorderWidth, mPaint);
            // 画出进度条
            mPaint.setColor(mDrawProgressColor);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(mBorderWidth);
    
            // 计算圆弧划过的角度
            float angle = CIRCULAR / mMaxProgress * mProgress;
            // 这里要画圆弧
            canvas.drawArc(mRectF, -90, angle, false, mPaint);
            // 画出另一部分的进度条
            mPaint.setColor(mBorderColor);
            mPaint.setStrokeWidth(mBorderWidth);
            // 这里要画圆弧
            canvas.drawArc(mRectF, -90 + angle, CIRCULAR - angle, false, mPaint);
        }
    
        /**
         * 开始过度动画
         */
        private synchronized void startIntermediateAnim() {
            if (valueAnimator != null && valueAnimator.isStarted()) {
                return;
            }
    
            if (valueAnimator == null) {
                valueAnimator = new ValueAnimator().ofFloat(mMinProgress, mMaxProgress - mMinProgress);
                valueAnimator.setDuration(DURATION);
                valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        float value = (float) valueAnimator.getAnimatedValue();
                        setProgress(value);
                        mStartAngle += 2;
                        invalidateSelf();
                    }
    
                });
                valueAnimator.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animator) {
    
                    }
    
                    @Override
                    public void onAnimationEnd(Animator animator) {
    
                    }
    
                    @Override
                    public void onAnimationCancel(Animator animator) {
                        // 充值旋转的角度
                        mStartAngle = -90;
                    }
    
                    @Override
                    public void onAnimationRepeat(Animator animator) {
                        // 互换颜色和位置
                        mStartAngle = mStartAngle - CIRCULAR / mMaxProgress * mMinProgress;
                        int color = getDrawProgressColor();
                        setDrawProgressColor(getDrawBorderColor());
                        setDrawBorderColor(color);
                    }
                });
            }
            // 开始动画的时候,要重新设置颜色,否则颜色可能会错乱,因为在动画的过程已经互换
            setDrawProgressColor(mProgressColor);
            setDrawBorderColor(mBorderColor);
            valueAnimator.setRepeatCount(-1);
            valueAnimator.start();
        }
    
        /**
         * 停止动画
         */
        private void stopAnim() {
            if (valueAnimator != null && valueAnimator.isRunning()) {
                valueAnimator.cancel();
            }
        }
    
        @Override
        public void release() {
            stopAnim();
        }
    
    }
    

    几乎是没有什么变化,增加了开始动画和结束动画方法,这样其他的状态时,可以节省系统资源。

    然后是工厂类:

    /**
     * Created by li.zhipeng on 2017/7/13.
     * <p>
     * 生产不同状态的Drawable的生产类
     */
    
    public class StatusDrawableFactory {
    
        private static StatusDrawableFactory mInstance;
    
        public synchronized static StatusDrawableFactory getInstance() {
            if (mInstance == null) {
                mInstance = new StatusDrawableFactory();
            }
            return mInstance;
        }
    
        /**
         * 返回指定状态的drawable
         * */
        public BaseStatusDrawable getDrawable(int status, CircleProgressSuperInfo info) {
            BaseStatusDrawable drawable = null;
            switch (status) {
                case Status.NORMAL:
                    drawable = new NormalDrawable(info);
                    break;
                case Status.LOADING:
                    drawable = new LoadingDrawable(info);
                    break;
                case Status.COMPLETE:
                    drawable = new CompleteDrawable(info);
                    break;
                case Status.ERROR:
                    drawable = new ErrorDrawable(info);
                    break;
    
            }
            return drawable;
        }
    
    }
    

    非常简单的单例模式,返回指定的BaseStatusDrawable类型。

    最后就是CircleProgressSuperBar:

    /**
     * Created by li.zhipeng on 2017/7/12.
     * <p>
     * 具有多状态的CircleProgressBar,整合前两个控件的效果
     */
    
    public class CircleProgressSuperBar extends View {
    
        /**
         * 保存四张状态的Drawable
         */
        private BaseStatusDrawable[] drawables = new BaseStatusDrawable[4];
    
        /**
         * 测试就只要一个xml的构造方法就足够了
         */
        public CircleProgressSuperBar(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
    
            // 初始化信息类
            drawables[Status.NORMAL] = StatusDrawableFactory.getInstance().getDrawable(Status.NORMAL,
                    new CircleProgressSuperInfo(Color.parseColor("#3399ff"), Color.parseColor("#3399ff"), 10));
            drawables[Status.LOADING] = StatusDrawableFactory.getInstance().getDrawable(Status.LOADING,
                    new CircleProgressSuperInfo(Color.parseColor("#ff0000"), Color.parseColor("#000000"), 10));
            drawables[Status.COMPLETE] = StatusDrawableFactory.getInstance().getDrawable(Status.COMPLETE,
                    new CircleProgressSuperInfo(Color.parseColor("#ffcc00"), Color.parseColor("#ffcc00"), 10));
            drawables[Status.ERROR] = StatusDrawableFactory.getInstance().getDrawable(Status.ERROR,
                    new CircleProgressSuperInfo(Color.parseColor("#ff3300"), Color.parseColor("#ff3300"), 10));
    
            drawables[Status.NORMAL].setText("Normal");
            drawables[Status.ERROR].setText("Error");
            drawables[Status.COMPLETE].setText("Complete");
    
            drawables[Status.NORMAL].setTextSize(42);
            drawables[Status.ERROR].setTextSize(42);
            drawables[Status.COMPLETE].setTextSize(42);
    
            drawables[Status.LOADING].setBorderWidth(20);
    
            // 设置重绘回调
            drawables[Status.LOADING].setCallback(this);
        }
    
        /**
         * 动画时长
         */
        private int mDuration = 500;
    
        /**
         * 目前的状态t
         */
        private int mCurrentStatus = Status.NORMAL;
    
        /**
         * 是否正在动画中
         */
        private boolean isAnim;
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            // 这里设置一些初始值
            int width = getMeasuredWidth();
            int height = getMeasuredHeight();
            drawables[Status.NORMAL].setWidth(width);
            drawables[Status.NORMAL].setHeight(height);
            drawables[Status.LOADING].setWidth(width);
            drawables[Status.LOADING].setHeight(height);
            drawables[Status.ERROR].setWidth(width);
            drawables[Status.ERROR].setHeight(height);
            drawables[Status.COMPLETE].setWidth(width);
            drawables[Status.COMPLETE].setHeight(height);
            // 设置的Radius
            int radius = width > height ? height / 2 : width / 2;
            drawables[Status.NORMAL].setMinSize(radius * 2);
            drawables[Status.LOADING].setMinSize(radius * 2);
            drawables[Status.ERROR].setMinSize(radius * 2);
            drawables[Status.COMPLETE].setMinSize(radius * 2);
            drawables[Status.LOADING].getInfo().setRadius(radius);
            drawables[Status.LOADING].getInfo().setPadding(width > height ? (width - radius * 2) / 2 : (height - radius * 2) / 2);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            // 画出不同状态的内容
            drawables[mCurrentStatus].draw(canvas);
        }
    
        /**
         * 设置状态
         */
        public void setStatus(int status) {
            if (mCurrentStatus != status && !isAnim) {
                // 这里设置动画效果
                changeStatus(mCurrentStatus, status);
                // 释放之前的动画
                this.drawables[mCurrentStatus].release();
                this.mCurrentStatus = status;
                this.drawables[mCurrentStatus].setIsAnim(true);
            }
        }
    
        /**
         * 状态改变的动画
         */
        private void changeStatus(int fromStatus, int toStatus) {
            isAnim = true;
            // 取出相关的动画信息
            CircleProgressSuperInfo fromStatusInfo = drawables[fromStatus].getInfo();
            CircleProgressSuperInfo toStatusInfo = drawables[toStatus].getInfo();
            // 开始动画
            AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.setDuration(mDuration);
            animatorSet.playTogether(AnimUtil.getColorAnim(fromStatusInfo.getBgColor(), toStatusInfo.getBgColor(), mDuration, colorUpdateListener),
                    AnimUtil.getColorAnim(fromStatusInfo.getBorderColor(), toStatusInfo.getBorderColor(), mDuration, borderColorUpdateListener),
                    AnimUtil.getRadiusAnim(fromStatusInfo.getRadius(), toStatusInfo.getRadius(), mDuration, radiusUpdateListener),
                    AnimUtil.getShapeAnim(fromStatusInfo.getPadding(), toStatusInfo.getPadding(), mDuration, shapeUpdateListener)
            );
            animatorSet.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
    
                }
    
                @Override
                public void onAnimationEnd(Animator animator) {
                    isAnim = false;
                    drawables[mCurrentStatus].setIsAnim(false);
                }
    
                @Override
                public void onAnimationCancel(Animator animator) {
    
                }
    
                @Override
                public void onAnimationRepeat(Animator animator) {
    
                }
            });
            animatorSet.start();
        }
    
        /**
         * color动画的回调
         */
        private ValueAnimator.AnimatorUpdateListener colorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                drawables[mCurrentStatus].setBgColor((Integer) valueAnimator.getAnimatedValue());
                invalidate();
            }
        };
    
        /**
         * borderColor动画的回调
         */
        private ValueAnimator.AnimatorUpdateListener borderColorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                drawables[mCurrentStatus].setBorderColor((Integer) valueAnimator.getAnimatedValue());
            }
        };
    
    
        /**
         * radius动画的回调
         */
        private ValueAnimator.AnimatorUpdateListener radiusUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                drawables[mCurrentStatus].setRadius((float) valueAnimator.getAnimatedValue());
            }
        };
    
        /**
         * shape动画的回调
         */
        private ValueAnimator.AnimatorUpdateListener shapeUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                drawables[mCurrentStatus].setPadding((Float) valueAnimator.getAnimatedValue());
            }
        };
    
    
        /**
         * Invalidates the specified Drawable.
         *
         * @param drawable the drawable to invalidate
         */
        @Override
        public void invalidateDrawable(@NonNull Drawable drawable) {
            invalidate();
        }
    
    }
    

    主要是找到各个状态的BaseStatusDrawable,然后取出信息,开始属性动画,但是有几个小知识点,你还记得吗?

    1、在获取指定状态的图片,直接使用Status.xxx作为数组的索引,保存和取出的速度都很快,是不是想起之前我们聊过的哈希表了?

    2、onMeasure方法里,记得使用getMeasuredXXX,因为getWidth和getHeight都是0,千万别忘了。

    有些朋友发现了:怎么突然冒出来一个invalidateDrawable()方法?我这里直说了,大家想自己去踩坑的可以试试:

    还记得之前说过的继承Drawable是为了重绘吗,如果是你只是调用了Drawable.invalidateSelf(),很遗憾的告诉你,是不可能重绘的,所以这里要重写这个方法,强制重绘。

    直接看源码就知道原因了:

    //Drawable的重绘方法,实际上是调用了callback,这样就和View解耦了
    public void invalidateSelf() {
            final Callback callback = getCallback();
            if (callback != null) {
                callback.invalidateDrawable(this);
            }
        }
    
    // view本身就实现了Callback
    public class View implements Drawable.Callback{
    
        // 请注意里面的判断
        @Override
        public void invalidateDrawable(@NonNull Drawable drawable) {
          // 满足了这个条件,才会重绘,所以要看看判断条件是什么
            if (verifyDrawable(drawable)) {
                final Rect dirty = drawable.getDirtyBounds();
                final int scrollX = mScrollX;
                final int scrollY = mScrollY;
    
                invalidate(dirty.left + scrollX, dirty.top + scrollY,
                        dirty.right + scrollX, dirty.bottom + scrollY);
                rebuildOutline();
            }
        }
        
    @CallSuper
        protected boolean verifyDrawable(@NonNull Drawable who) {
        // 这里就是判断,view要判断是否使用了这个Drawable,如果没有使用,就不去重绘了,这个理论都是可以理解的。
            return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
        }
    }
    
    

    因为我们仅仅是canvas绘图,并没有设置背景或者是前景图片之类的东西,自然就无法重绘,也就是重写invalidateDrawable()方法的原因。

    还有两个类,没有贴出来:Status(Drawable的状态),AnimUtil(动画工具类),因为感觉今天的内容已经很长了,所以就省略了把,大家可以在demo中去查看。

    总结#

    看的说的挺溜,其实在写的时候还是出现了很多问题的,而且现在也还存在一些小问题,如果你发现博客中的代码和demo中有一点点区别,那就是我后来又修改了,但是主要思想是不会变了,大家可以自己去设置自定义属性,这样我们的完成度就更完美了。

    github地址

    相关文章

      网友评论

        本文标题:Android动画篇(四):最终效果篇CircleProgres

        本文链接:https://www.haomeiwen.com/subject/foacsxtx.html