Android模拟糟糕音量控制设计大赛之ProgressView

效果图

ScreenGif4.gif
  • 可以通过旋转view或者直接拖动来控制进度。
  • 根据旋转角度,音量的移动速度会改变
  • 根据旋转角度,归位时候的速度会改变

实现流程

  1. 重写onMeasure(),使得高度为外部矩形的高度+padding。
  2. 重写onDraw(),绘制两个矩形和一个球。
  3. 重写onTouchEvent()判断是点击小球移动还是旋转控件移动,并且判断点击是控件左半部分还是右半部分,在手指抬起时,执行归为动画。
  4. 设置音量改变接口供外部使用。

使用

记得要在外层Linearlayout中要添加clipChildred=false。。



    
    

代码

public class ProgressView extends View {
    private static final String TAG = "HappyVoiceView";
    /**
     * 外层矩形框画笔
     */
    private Paint outRectPaint;
    /**
     * 内层矩形框画笔
     */
    private Paint innerRectPaint;
    /**
     * 音量控制球画笔
     */
    private Paint ballPaint;
    /**
     * 外层,内层,球的矩形范围
     */
    private RectF rectF1, rectF2, ballRect;
    /**
     * 外层矩形高度
     */
    private int outRectHeight = 50;
    /**
     * 内层矩形框画笔
     */
    private int innerRectHeight = 20;
    /**
     * 小球的半径
     */
    private int circleRadius = 15;
    /**
     * 内层小球可移动范围
     */
    private int length = 0;
    /**
     * 是否是旋转控件
     */
    private boolean doRoate = false;
    /**
     * 竖直方向偏移
     */
    private float downY;
    /**
     * 角度
     */
    private int degress = 0;
    /**
     * 手指抬起归位
     */
    private ValueAnimator valueAnimator;
    /**
     * 小球当前位置
     */
    private int ballCurrentLength = 0;
    /**
     * 从左面旋转控件
     */
    private boolean isTouchLeft = false;
    /**
     * 从右面面旋转控件
     */
    private boolean isTouchBall = false;
    /**
     * 小球移动最终速度
     */
    private int speed = 1;
    /**
     * 小球最小速度
     */
    private int minSpeed = 2;
    /**
     * 音量改变监听
     */
    private OnVoiceUpdateLinstener voiceUpdateLinstener;

    public void setVoiceUpdateLinstener(OnVoiceUpdateLinstener voiceUpdateLinstener) {
        this.voiceUpdateLinstener = voiceUpdateLinstener;
    }

    public ProgressView(Context context) {
        super(context);
        init();
    }
    public ProgressView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public ProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        setMeasuredDimension(widthSize, outRectHeight + getPaddingTop() + getPaddingBottom());

    }
    private void init() {
        outRectPaint = initPaint();
        outRectPaint.setColor(Color.CYAN);
        innerRectPaint = initPaint();
        innerRectPaint.setColor(Color.YELLOW);
        ballPaint = initPaint();
        ballPaint.setColor(Color.RED);
        ballRect = new RectF();
    }
    private void initValueAnimator() {
        valueAnimator = ValueAnimator.ofInt(degress, 0);
          valueAnimator.setDuration(Math.abs(degress)/10*100);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                degress = (int) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.start();
    }
    private Paint initPaint() {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  //抗锯齿
        paint.setDither(true);  //防抖动
        paint.setColor(Color.CYAN);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeCap(Paint.Cap.SQUARE);
        return paint;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
        dealBallLength();
        rectF1 = new RectF((-getMeasuredWidth() / 2) + getPaddingLeft(), -outRectHeight / 2, (getMeasuredWidth() / 2) - getPaddingLeft(), outRectHeight / 2);
        rectF2 = new RectF((-getMeasuredWidth() / 2) + getPaddingLeft() + getPaddingLeft(), -innerRectHeight / 2, (getMeasuredWidth() / 2) - getPaddingLeft() - getPaddingLeft(), innerRectHeight / 2);
        length = (int) rectF2.width();
        canvas.rotate(degress);
        canvas.drawRoundRect(rectF1, 10, 10, outRectPaint);
        canvas.drawRoundRect(rectF2, 20, 20, innerRectPaint);
        canvas.drawCircle((rectF2.left + ballCurrentLength) + circleRadius / 2, rectF2.centerY(), circleRadius, ballPaint);
        ballRect.left = (rectF2.left + ballCurrentLength) + circleRadius / 2 - circleRadius;
        ballRect.right = (rectF2.left + ballCurrentLength) + circleRadius / 2 + circleRadius;
        ballRect.top = rectF2.centerY() - circleRadius;
        ballRect.bottom = rectF2.centerY() + circleRadius;

        if (voiceUpdateLinstener != null) {
            voiceUpdateLinstener.onVoiceChanged((int) ((float) ballCurrentLength / length * 100));
        }
    }
    private int dealBallLength() {
        speed = Math.abs(degress) / 3 + minSpeed;
        if (degress > 0 && ballCurrentLength < length) {
            speed = ballCurrentLength + speed > length ? (length - ballCurrentLength) : speed;
            ballCurrentLength += speed;
            invalidate();
        } else if (degress < 0 && ballCurrentLength > 0) {
            speed = ballCurrentLength - speed < 0 ? ballCurrentLength : speed;
            ballCurrentLength -= speed;
            invalidate();
        }

        return ballCurrentLength;
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float y = event.getY();
        float x = event.getX();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (valueAnimator != null && valueAnimator.isRunning()) {
                    return false;
                }
                if (ballRect.contains(event.getX() - getMeasuredWidth() / 2, y - getMeasuredHeight() / 2)) {
                    isTouchBall = true;
                    break;
                }
                if (rectF1.contains(event.getX() - getMeasuredWidth() / 2, y - getMeasuredHeight() / 2)) {   //平移过坐标系
                    doRoate = true;
                    downY = (int) event.getY();
                    if (event.getX() - getMeasuredWidth() / 2 <= 0) {
                        isTouchLeft = true;
                    } else {
                        isTouchLeft = false;
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (isTouchBall) {
                    ballCurrentLength = (int) (x - getPaddingLeft() - getPaddingLeft());
                    if (ballCurrentLength < 0) {
                        ballCurrentLength = 0;
                    } else if (ballCurrentLength > length) {
                        ballCurrentLength = length;
                    }
                    invalidate();
                    break;
                }
                x = (float) Math.atan((y - downY) / rectF1.right);
                degress = (int) Math.toDegrees(x);
                degress = isTouchLeft ? -degress : degress;
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                if (doRoate) {
                    initValueAnimator();
                }
                doRoate = false;
                isTouchBall = false;
                break;
        }
        return true;
    }
    public interface OnVoiceUpdateLinstener {
        void onVoiceChanged(int voice);
    }
}

你可能感兴趣的:(Android模拟糟糕音量控制设计大赛之ProgressView)