[置顶] Android自定义控件 温度旋转按钮

来源:互联网 发布:java学多久可以上岗 编辑:程序博客网 时间:2024/06/06 01:31
 

[置顶] Android自定义控件 温度旋转按钮

标签: android自定义温度旋转按钮
 1495人阅读 评论(6) 收藏 举报
 分类:

目录(?)[+]

1.介绍

首先看下效果图

温度旋转按钮

2.思路

  1. 初始化一些参数
  2. 绘制刻度盘
  3. 绘制刻度盘下的圆弧
  4. 绘制标题与温度标识
  5. 绘制旋转按钮
  6. 绘制温度
  7. 处理滑动事件
  8. 提供一些接口方法

3.实现

初始化一些参数

public class TempControlView extends View {    // 控件宽    private int width;    // 控件高    private int height;    // 刻度盘半径    private int dialRadius;    // 圆弧半径    private int arcRadius;    // 刻度高    private int scaleHeight = dp2px(10);    // 刻度盘画笔    private Paint dialPaint;    // 圆弧画笔    private Paint arcPaint;    // 标题画笔    private Paint titlePaint;    // 温度标识画笔    private Paint tempFlagPaint;    // 旋转按钮画笔    private Paint buttonPaint;    // 温度显示画笔    private Paint tempPaint;    // 文本提示    private String title = "最高温度设置";    // 温度    private int temperature;    // 最低温度    private int minTemp = 15;    // 最高温度    private int maxTemp = 30;    // 四格(每格4.5度,共18度)代表温度1度    private int angleRate = 4;    // 按钮图片    private Bitmap buttonImage = BitmapFactory.decodeResource(getResources(),            R.mipmap.btn_rotate);    // 按钮图片阴影    private Bitmap buttonImageShadow = BitmapFactory.decodeResource(getResources(),            R.mipmap.btn_rotate_shadow);    // 抗锯齿    private PaintFlagsDrawFilter paintFlagsDrawFilter;    // 温度改变监听    private OnTempChangeListener onTempChangeListener;    // 以下为旋转按钮相关    // 当前按钮旋转的角度    private float rotateAngle;    // 当前的角度    private float currentAngle;    ...    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        // 控件宽、高        width = height = Math.min(h, w);        // 刻度盘半径        dialRadius = width / 2 - dp2px(20);        // 圆弧半径        arcRadius = dialRadius - dp2px(20);    }    ...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

绘制刻度盘

以屏幕中心为画布原点,圆弧角度为270°,绘制未选中与选中状态的刻度盘。 
旋转方法中多减的2°是后期调整所得,不用在意。

/** * 绘制刻度盘 * * @param canvas 画布 */private void drawScale(Canvas canvas) {    canvas.save();    canvas.translate(getWidth() / 2, getHeight() / 2);    // 逆时针旋转135-2度    canvas.rotate(-133);    dialPaint.setColor(Color.parseColor("#3CB7EA"));    for (int i = 0; i < 60; i++) {        canvas.drawLine(0, -dialRadius, 0, -dialRadius + scaleHeight, dialPaint);        canvas.rotate(4.5f);    }    canvas.rotate(90);    dialPaint.setColor(Color.parseColor("#E37364"));    for (int i = 0; i < (temperature - minTemp) * angleRate; i++) {        canvas.drawLine(0, -dialRadius, 0, -dialRadius + scaleHeight, dialPaint);        canvas.rotate(4.5f);    }    canvas.restore();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

刻度盘

绘制刻度盘下的圆弧

/** * 绘制刻度盘下的圆弧 * * @param canvas 画布 */private void drawArc(Canvas canvas) {    canvas.save();    canvas.translate(getWidth() / 2, getHeight() / 2);    canvas.rotate(135 + 2);    RectF rectF = new RectF(-arcRadius, -arcRadius, arcRadius, arcRadius);    canvas.drawArc(rectF, 0, 265, false, arcPaint);    canvas.restore();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

圆弧

绘制标题与温度标识

    /**     * 绘制标题与温度标识     *     * @param canvas 画布     */    private void drawText(Canvas canvas) {        canvas.save();        // 绘制标题        float titleWidth = titlePaint.measureText(title);        canvas.drawText(title, (width - titleWidth) / 2, dialRadius * 2 + dp2px(15), titlePaint);        // 绘制最小温度标识        // 最小温度如果小于10,显示为0x        String minTempFlag = minTemp < 10 ? "0" + minTemp : minTemp + "";        float tempFlagWidth = titlePaint.measureText(maxTemp + "");        canvas.rotate(55, width / 2, height / 2);        canvas.drawText(minTempFlag, (width - tempFlagWidth) / 2, height + dp2px(5), tempFlagPaint);        // 绘制最大温度标识        canvas.rotate(-105, width / 2, height / 2);        canvas.drawText(maxTemp + "", (width - tempFlagWidth) / 2, height + dp2px(5), tempFlagPaint);        canvas.restore();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

标题与温度标识

绘制旋转按钮

/** * 绘制旋转按钮 * * @param canvas 画布 */private void drawButton(Canvas canvas) {    // 按钮宽高    int buttonWidth = buttonImage.getWidth();    int buttonHeight = buttonImage.getHeight();    // 按钮阴影宽高    int buttonShadowWidth = buttonImageShadow.getWidth();    int buttonShadowHeight = buttonImageShadow.getHeight();    // 绘制按钮阴影    canvas.drawBitmap(buttonImageShadow, (width - buttonShadowWidth) / 2,            (height - buttonShadowHeight) / 2, buttonPaint);    Matrix matrix = new Matrix();    // 设置按钮位置    matrix.setTranslate(buttonWidth / 2, buttonHeight / 2);    // 设置旋转角度    matrix.preRotate(45 + rotateAngle);    // 按钮位置还原,此时按钮位置在左上角    matrix.preTranslate(-buttonWidth / 2, -buttonHeight / 2);    // 将按钮移到中心位置    matrix.postTranslate((width - buttonWidth) / 2, (height - buttonHeight) / 2);    //设置抗锯齿    canvas.setDrawFilter(paintFlagsDrawFilter);    canvas.drawBitmap(buttonImage, matrix, buttonPaint);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

旋转按钮

绘制温度

/** * 绘制温度 * * @param canvas 画布 */private void drawTemp(Canvas canvas) {    canvas.save();    canvas.translate(getWidth() / 2, getHeight() / 2);    float tempWidth = tempPaint.measureText(temperature + "");    float tempHeight = (tempPaint.ascent() + tempPaint.descent()) / 2;    canvas.drawText(temperature + "°", -tempWidth / 2 - dp2px(5), -tempHeight, tempPaint);    canvas.restore();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

温度

处理滑动事件

private boolean isDown;private boolean isMove;@Overridepublic boolean onTouchEvent(MotionEvent event) {    switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            isDown = true;            float downX = event.getX();            float downY = event.getY();            currentAngle = calcAngle(downX, downY);            break;        case MotionEvent.ACTION_MOVE:            isMove = true;            float targetX;            float targetY;            downX = targetX = event.getX();            downY = targetY = event.getY();            float angle = calcAngle(targetX, targetY);            // 滑过的角度增量            float angleIncreased = angle - currentAngle;            // 防止越界            if (angleIncreased < -270) {                angleIncreased = angleIncreased + 360;            } else if (angleIncreased > 270) {                angleIncreased = angleIncreased - 360;            }            IncreaseAngle(angleIncreased);            currentAngle = angle;            invalidate();            break;        case MotionEvent.ACTION_CANCEL:        case MotionEvent.ACTION_UP: {            if (isDown && isMove) {                // 纠正指针位置                rotateAngle = (float) ((temperature - minTemp) * angleRate * 4.5);                invalidate();                // 回调温度改变监听                onTempChangeListener.change(temperature);                isDown = false;                isMove = false;            }            break;        }    }    return true;}/** * 以按钮圆心为坐标圆点,建立坐标系,求出(targetX, targetY)坐标与x轴的夹角 * * @param targetX x坐标 * @param targetY y坐标 * @return (targetX, targetY)坐标与x轴的夹角 */private float calcAngle(float targetX, float targetY) {    float x = targetX - width / 2;    float y = targetY - height / 2;    double radian;    if (x != 0) {        float tan = Math.abs(y / x);        if (x > 0) {            if (y >= 0) {                radian = Math.atan(tan);            } else {                radian = 2 * Math.PI - Math.atan(tan);            }        } else {            if (y >= 0) {                radian = Math.PI - Math.atan(tan);            } else {                radian = Math.PI + Math.atan(tan);            }        }    } else {        if (y > 0) {            radian = Math.PI / 2;        } else {            radian = -Math.PI / 2;        }    }    return (float) ((radian * 180) / Math.PI);}/** * 增加旋转角度 * * @param angle 增加的角度 */private void IncreaseAngle(float angle) {    rotateAngle += angle;    if (rotateAngle < 0) {        rotateAngle = 0;    } else if (rotateAngle > 270) {        rotateAngle = 270;    }    temperature = (int) (rotateAngle / 4.5) / angleRate + minTemp;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104

提供一些接口方法

/** * 设置温度 * * @param minTemp 最小温度 * @param maxTemp 最大温度 * @param temp    设置的温度 */public void setTemp(int minTemp, int maxTemp, int temp) {    this.minTemp = minTemp;    this.maxTemp = maxTemp;    this.temperature = temp;    this.angleRate = 60 / (maxTemp - minTemp);    rotateAngle = (float) ((temp - minTemp) * angleRate * 4.5);    invalidate();}/** * 设置温度改变监听 * * @param onTempChangeListener 监听接口 */public void setOnTempChangeListener(OnTempChangeListener onTempChangeListener) {    this.onTempChangeListener = onTempChangeListener;}/** * 温度改变监听接口 */public interface OnTempChangeListener {    /**     * 回调方法     *     * @param temp 温度     */    void change(int temp);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

4.写在最后

源码已托管到GitHub上,欢迎Fork,觉得还不错就Start一下吧!

点击下载源码

GitHub地址:https://github.com/alidili/TempControlView

欢迎同学们吐槽评论,如果你觉得本篇博客对你有用,那么就留个言或者顶一下吧(^-^)

原创粉丝点击