美文网首页Android自定义View自定义控件
android自定义view实现进度条动画、按钮渐变及录制状态控

android自定义view实现进度条动画、按钮渐变及录制状态控

作者: 码仔时光 | 来源:发表于2020-07-04 14:18 被阅读0次

    不得不说自定义view绘制各种图形及它们的动画是令人有成就感的事情。该篇来分享之前项目中写的一个视频录制按钮的自定义view,该view有录制、暂停(结束)等状态的控制,各种状态转变的动画,以及录制进度展示。先看效果(圆圈的小白色段是最小录制时长的标记点):


    录制效果

    代码的内容其实主要就是绘制进度条、录制按钮的切换动画(圆角正方形—>圆—>圆角正方形)。还是那句话,计算好每个组件的坐标(半径)事情就完成了一半,所以关键在于计算,这里的图形都简单计算并不复杂,只是要稍微细心点。注释都写得比较详细,就不一一解释了,具体看代码:

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.LinearGradient;
    import android.graphics.Paint;
    import android.graphics.RectF;
    import android.graphics.Shader;
    import android.util.AttributeSet;
    import android.view.View;
    import androidx.annotation.Nullable;
    import com.robot.common.frame.BaseApp;
    import com.robot.common.utils.PixelUtil;
    
    /**
     * 视频录制控制、进度
     *
     * @author ly
     * date 2020/3/3 11:02
     */
    public class RecordProgressView extends View implements View.OnClickListener {
    
        private static final float changeRatio = 5f;
        //最小录制时长标记的弧长
        private static final int minPAngle = 2;
    
        private OnRecordStatusListener onRecordStatusListener;
        private Paint paint;
        //内圈录制、暂停按钮颜色
        private int circleInsideColor;
        //进度圈颜色
        private int circleOutsideColor;
        //进度颜色
        private int circleProgressColor;
        private float progressW;
        //进度条圆圈半径
        private float circleProgressR;
        //内实心圆圈半径
        private float circleInsideR;
        //录制按钮宽高
        private float pauseRectW;
    
        //动态变量内圈半径(逐渐变小或变大)
        private float curCircleInsideR;
    
        private State state = State.READY;
        //暂停按钮rect
        private RectF pauseRect;
        private int pauseRadius;
        private RectF progressRect;
        private float progress;
        private float minProgress = 90;
        private float minCircleR;
        private LinearGradient linearGradientCircle;
        private LinearGradient linearGradientRect;
    
        public enum State {
            READY, RECORDING, PAUSE, FINISH
        }
    
        public RecordProgressView(Context context) {
            super(context);
            init();
        }
    
        public RecordProgressView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public RecordProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            circleInsideColor = 0xffE9B763;
            circleOutsideColor = 0x55ffffff;
            circleProgressColor = 0xffffffff;
            paint = new Paint();
            paint.setAntiAlias(true);//抗锯齿
            pauseRadius = PixelUtil.dp2px(5);
    
            setOnClickListener();
        }
    
        public float getProgress() {
            return progress;
        }
    
        /**
         * @param progress 已录制时长/最大时长
         * @author ly on 2020/3/3 16:41
         */
        public void setProgress(float progress) {
            this.progress = progress * 360;
            if (this.progress >= 360) {//表示录制达到最大时长,自动结束
                this.progress = 360;
                state = State.FINISH;
                if (onRecordStatusListener != null)
                    onRecordStatusListener.onFinish();
            }
            invalidate();
        }
    
        /**
         * @param minProgress 最小录制时长/最大时长
         * @author ly on 2020/3/3 16:41
         */
        public void setMinProgress(float minProgress) {
            this.minProgress = minProgress * 360;
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            if (circleProgressR == 0) {
                int w = getWidth();
                progressW = w * 0.061f;
                circleProgressR = (w - progressW) / 2f;
                circleInsideR = (w * 0.83f) / 2;
                pauseRectW = w * 0.37f;
                curCircleInsideR = circleInsideR;
                //矩形对角线的一半
    //            minCircleR = (float) (Math.sqrt(Math.pow(pauseRectW, 2) * 2) / 2f);
                minCircleR = pauseRectW / 2;
    
                pauseRect = new RectF(w / 2f - pauseRectW / 2
                        , w / 2f - pauseRectW / 2
                        , w / 2f + pauseRectW / 2
                        , w / 2f + pauseRectW / 2);
                progressRect = new RectF(0 + progressW / 2, 0 + progressW / 2, w - progressW / 2, w - progressW / 2);
    
                float offsetX = (float) Math.sqrt(Math.pow(curCircleInsideR, 2) / 2);
    
                //圆的左上到右下方向渐变
                linearGradientCircle = new LinearGradient(
                        curCircleInsideR - offsetX, curCircleInsideR - offsetX, w - offsetX, w - offsetX,
                        0xff7360FF, 0xff9663F3,
                        Shader.TileMode.MIRROR);
    
                linearGradientRect = new LinearGradient(
                        pauseRect.left, pauseRect.top, pauseRect.right, pauseRect.bottom,
                        0xff7360FF, 0xff9663F3,
                        Shader.TileMode.MIRROR);
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            //画外圈进度圈、进度
            paint.setShader(null);
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(circleOutsideColor);
            paint.setStrokeWidth(progressW);//设置画笔粗细
            canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, circleProgressR, paint);
    
            if (minProgress - minPAngle > 0 && BaseApp.get().isDebug()) {
                paint.setColor(circleProgressColor);
                canvas.drawArc(progressRect, minProgress - 90, minPAngle, false, paint);
            }
    
            if (state != State.READY) {
                //进度就是圆弧
                //时钟3点的方向为0度,顺时钟方向为正,-90是圆弧的开始点,即12点位置开始,
                //sweepAngle扫过的角度,调整该值即可实现进度顺时针加载(0-360)
                paint.setColor(circleProgressColor);
                canvas.drawArc(progressRect, -90, progress, false, paint);
            }
    
            //画内部圆/暂停按钮
            paint.setColor(circleInsideColor);
            paint.setStyle(Paint.Style.FILL);
            paint.setShader(linearGradientCircle);
            if (state == State.RECORDING) {//录制中画矩形和进度
                if (curCircleInsideR > minCircleR) {
                    canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, curCircleInsideR, paint);
                    changeCircleR(false);
                } else {
                    paint.setShader(linearGradientRect);
                    canvas.drawRoundRect(pauseRect, pauseRadius, pauseRadius, paint);
                }
            } else {
                canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, curCircleInsideR, paint);
                if (curCircleInsideR < circleInsideR)
                    changeCircleR(true);
            }
        }
    
    
        public void resetState() {
            state = State.READY;
            progress = 0;
            invalidate();
        }
    
        public void setState(State state) {
            this.state = state;
            invalidate();
        }
    
        public void setOnClickListener() {
            setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (state) {
                //由开始状态变为录制状态,点击后由圆变为矩形
                case READY:
                case PAUSE:
                    state = State.RECORDING;
                    if (onRecordStatusListener != null)
                        onRecordStatusListener.onRecording();
    
                    invalidate();
                    break;
                //录制中,点击后由矩形变为圆
                case RECORDING:
                    if (progress < minProgress) {
                        state = State.PAUSE;
                        if (onRecordStatusListener != null)
                            onRecordStatusListener.onPause();
                    } else {
                        state = State.FINISH;
                        if (onRecordStatusListener != null)
                            onRecordStatusListener.onFinish();
    
                    }
                    invalidate();
                    break;
                default:
    
                    break;
            }
        }
    
        private void changeCircleR(boolean is2Big) {
            if (is2Big) {//变大
                curCircleInsideR += changeRatio;
                if (curCircleInsideR >= circleInsideR)
                    curCircleInsideR = circleInsideR;
            } else {//变小
                curCircleInsideR -= changeRatio;
                if (curCircleInsideR <= minCircleR)
                    curCircleInsideR = minCircleR;
            }
            invalidate();
        }
    
        public void setOnRecordStatusListener(OnRecordStatusListener onRecordStatusListener) {
            this.onRecordStatusListener = onRecordStatusListener;
        }
    
        public interface OnRecordStatusListener {
            void onRecording();
    
            void onPause();
    
            void onFinish();
        }
    }
    

    如果对大家有帮助,请点个赞以鼓励我前进~

    相关文章

      网友评论

        本文标题:android自定义view实现进度条动画、按钮渐变及录制状态控

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