Android canvas绘制的可滑动刻度尺

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android canvas绘制的可滑动刻度尺相关的知识,希望对你有一定的参考价值。

参考技术A

首先放上所有成员变量

以上用到的方法

本Demo是使用Canvas进行绘制刻度,为了适配不同宽度的屏幕,所以小的刻度使用从中心指针处向两边绘制到屏幕边界的方法,使用GestureDetector接管View的触摸事件,在onScroll中修改体重值并不断进行重绘,达到刻度尺滚动的效果。并在onFling中接收抛动的事件,开启一个属性动画达到平滑的效果。由于我没有在GestureDetector中找到能响应不fling的up事件,所以要自己手动在onTouchEvent中写ACIONT_UP时的动作

遮罩层的实现方式,是drawRec 一个整个画布大小的白色矩形,使用线性渐变,两边不透明到中间透明。注意,由于使用了PorterDuff.Mode.MULTIPLY来合成,所以需要使用到离屏缓冲,关于离屏缓冲可以看看 HenCoder
最后附上github: https://github.com/Pro47x/BodyWeightScaleTableView

基于 HTML5 Canvas 的可交互旋钮组件

前言

此次的 Demo 效果如下:

技术图片

Demo 链接:https://hightopo.com/demo/comp-knob/

整体思路

  • 组件参数
  • 绘制旋钮
  • 绘制刻度
  • 绘制指针
  • 绘制标尺
  • 绘制文本

1.组件参数

技术图片

 

以下是下文会使用到的部分变量,在此先贴出来

var origin, // 原点
    percent, // 显示刻度占总刻度的百分比
    partAngle, // 每个刻度所占的角度
    startAngle, //刻度起始的角度
    calibrationPoints, // 每个刻度的信息
    pointer, // 指针的信息
    scaleLine, // 标尺的信息
    calibrationColors // 刻度渐变色

2.绘制旋钮

这里主要就使用了 canvas api 中的 arc()createRadialGradient()

主要代码:

g.beginPath();
var ringRadial = g.createRadialGradient(origin.x, origin.y, 0, origin.x, origin.y, ringRadio);
ringRadial.addColorStop(0, ht.Default.brighter(ringColor, 20));
ringRadial.addColorStop(0.95, ht.Default.brighter(ringColor, 40));
ringRadial.addColorStop(1, ht.Default.darker(ringColor, 20));

var borderRadial = g.createRadialGradient(origin.x, origin.y, ringRadio - ringBorderWidth / 2, origin.x, origin.y, ringRadio + ringBorderWidth / 2);
borderRadial.addColorStop(0, ht.Default.brighter(ringBorderColor, 2));
borderRadial.addColorStop(0.5, ht.Default.brighter(ringBorderColor, 4));
borderRadial.addColorStop(1, ht.Default.darker(ringBorderColor, 4));
g.fillStyle = ringRadial;
g.lineWidth = ringBorderWidth;
g.strokeStyle = borderRadial;
g.arc(origin.x, origin.y, ringRadio, 0, 2 * Math.PI);
g.closePath();
g.fill();
g.stroke();

效果图:

技术图片

3.绘制刻度

这里绘制每个刻度采用的是绘制路径的方法,所以声明了一个变量 calibrationPoints 用来存放每个刻度的起始点坐标,根据配置的参数去计算 calibrationPoints 的信息。

主要代码:

var calibrationPoints = [];
for (var i = 0; i < calibrationCount + 1; i++) 
    var point = 
        startx: origin.x + (ringRadio + ringBorderWidth + 0 * calibrationHeight) * Math.cos(startAngle - i * partAngle),
        starty: origin.y - (ringRadio + ringBorderWidth + 0 * calibrationHeight) * Math.sin(startAngle - i * partAngle),
        endx: origin.x + (ringRadio + ringBorderWidth + 1 * calibrationHeight) * Math.cos(startAngle - i * partAngle),
        endy: origin.y - (ringRadio + ringBorderWidth + 1 * calibrationHeight) * Math.sin(startAngle - i * partAngle)
    ;
    if (i <= (calibrationCount * percent) && percent > 0) 
        point.show = true;
     else 
        point.show = false;
    
    calibrationPoints.push(point);

有了每个刻度的信息后,接下来就开始绘制刻度。首先绘制了所有的刻度,然后替换画笔颜色为高亮色,再绘制当前需要高亮的刻度。

主要代码:

calibrationPoints.forEach(function (i, index) 
    g.beginPath();
    if (calibrationColorWheelShow) 
        calibrationBrightColor = calibrationColors[index];
    
    g.lineWidth = 1.2 * calibrationWidth;
    g.strokeStyle = i.show ? calibrationBrightColor : ht.Default.brighter(calibrationDarkColor, 10);

    g.moveTo(i.startx, i.starty);
    g.lineTo(i.endx, i.endy);
    g.closePath();
    g.stroke();
)

calibrationPoints.forEach(function (i, index) 
    g.beginPath();
    if (calibrationColorWheelShow) 
        calibrationBrightColor = calibrationColors[index];
    
    g.lineWidth = calibrationWidth;
    g.strokeStyle = i.show ? calibrationBrightColor : calibrationDarkColor;

    g.moveTo(i.startx, i.starty);
    g.lineTo(i.endx, i.endy);
    g.closePath();
    g.stroke();
)

效果图:

技术图片

考虑到一种高亮颜色太单调,于是加了个色轮。思路:给每个刻度都添加了颜色的标识。

主要代码:

if (calibrationColorWheelShow)  // 显示刻度色轮
    var colors = [];
    calibrationColorWheel.forEach(function (i) 
        colors.push(ht.Default.toColorData(i))
    )
    // 把颜色值转换成rgb方式,设定多少秒改变完成,每次改变多少值,计算需要多少次
    // ,比如rba(x,y,z)到rgb(a,b,c),假设需要100次,那么每次设定
    // rgb((a-x)/100+x,(b-y)/100+y,(c-z)/100+z)
    var count = Math.ceil(calibrationCount / (calibrationColorWheel.length - 1)); // 渐变次数
    calibrationColors = [];
    for (var i = 0; i < colors.length - 1; i++) 
        for (var j = 1; j <= count; j++) 
            var item = rgb(
                + Math.round((colors[i + 1][0] - colors[i][0]) / j + colors[i][0])
                + ,
                + Math.round((colors[i + 1][1] - colors[i][1]) / j + colors[i][1])
                + ,
                + Math.round((colors[i + 1][2] - colors[i][2]) / j + colors[i][2])
                + );
            calibrationColors.push(item)
        
    

效果图:

技术图片

4.绘制指针

这个主要是根据三角函数去计算相对圆心的偏移角度,按照当前值和刻度最大值的比例来计算偏移量,然后换算成对应的坐标。

主要代码:

pointer = 
    x: origin.x + (ringRadio - pointerRadio - ringBorderWidth) * Math.cos(startAngle - Math.PI * 2 * calibrationPercent * percent),
    y: origin.y - (ringRadio - pointerRadio - ringBorderWidth) * Math.sin(startAngle - Math.PI * 2 * calibrationPercent * percent),
    r: pointerRadio,
    color: percent > 0 ? calibrationBrightColor : calibrationDarkColor,
    show: true,


if (pointerShow) 
    g.beginPath();
    g.fillStyle = pointer.color;
    g.arc(pointer.x, pointer.y, pointer.r, 0, Math.PI * 2);
    g.closePath();
    g.fill();

效果图:

技术图片

5.绘制标尺

计算标尺角度的算法同指针。

主要代码:

scaleLine = 
    startx: origin.x,
    starty: origin.y,
    endx: origin.x + (ringRadio + ringBorderWidth + 2 * calibrationHeight) * Math.cos(startAngle - Math.PI * 2 * calibrationPercent * percent),
    endy: origin.y - (ringRadio + ringBorderWidth + 2 * calibrationHeight) * Math.sin(startAngle - Math.PI * 2 * calibrationPercent * percent),
    color: percent > 0 ? calibrationBrightColor : calibrationDarkColor,
    show: scaleLineShow,

if (scaleLine) 
    g.beginPath();
    g.strokeStyle = ‘red‘;
    g.setLineDash([1, 2]);
    g.lineWidth = 0.5 * calibrationWidth;
    g.moveTo(scaleLine.startx, scaleLine.starty);
    g.lineTo(scaleLine.endx, scaleLine.endy);
    g.closePath();
    g.stroke();

效果图:

技术图片

6.绘制文本

主要代码:

if (labelShow) 
    var text = ht.Default.getTextSize(font, value);
    g.fillStyle = labelColor;
    g.font = font;
    g.fillText(value.toFixed(2), labelDot.x, labelDot.y);

效果图:

技术图片

到这就完成了基本的旋钮组件,下面继续做一些细节上的优化。

主要代码:

var backgroundRadial = g.createRadialGradient(x + 0.5 * width, y + 0.2 * height, 0, x + 0.5 * width, y + 0.2 * height, Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height, 2)));
backgroundRadial.addColorStop(0, ‘rgba(220,220,220,1)‘);
backgroundRadial.addColorStop(1, backgroundColor);
g.fillStyle = backgroundRadial;
g.fillRect(x, y, width, height);

g.beginPath();
var ringRadial = g.createRadialGradient(origin.x, origin.y - ringRadio / 2, 0, origin.x, origin.y - ringRadio / 2, 1.5 * ringRadio);
ringRadial.addColorStop(0, ht.Default.brighter(ringColor, 40));
// ringRadial.addColorStop(0.25, ht.Default.brighter(ringColor, 40));
ringRadial.addColorStop(1, ht.Default.darker(ringColor, 20));

var borderRadial = g.createRadialGradient(origin.x, origin.y, ringRadio - ringBorderWidth / 2, origin.x, origin.y, ringRadio + ringBorderWidth / 2);
borderRadial.addColorStop(0, ht.Default.brighter(ringBorderColor, 2));
borderRadial.addColorStop(0.5, ht.Default.brighter(ringBorderColor, 4));
borderRadial.addColorStop(1, ht.Default.darker(ringBorderColor, 4));
g.fillStyle = ringRadial;

g.lineWidth = ringBorderWidth;
g.strokeStyle = borderRadial;
g.arc(origin.x, origin.y, ringRadio, 0, 2 * Math.PI);
g.closePath();
g.fill();
g.shadowBlur = 20;
g.shadowColor = shadowColor;
g.shadowOffsetY = ringBorderWidth;
g.stroke();
g.shadowBlur = 0;
g.shadowOffsetY = 0;

效果图:

技术图片

 

以上是关于Android canvas绘制的可滑动刻度尺的主要内容,如果未能解决你的问题,请参考以下文章

Android实现滑动刻度尺效果,选择身高体重和生日

Android UI-薄荷健康尺子

h5页面实现可滑动刻度尺

Canvas入门08-绘制仪表盘

Android仿华为天气绘制刻度盘

Android仿华为天气绘制刻度盘