delphi中griddata控件写入float数值_无极旋钮控件,隔壁产品都馋哭了

33c6a56c1d67615ca45f3e6d5e6f7e53.png

阅读完本文约需6分钟。

废不说,看图

cfe125ec503b65b3d9907f2bcc66e6a7.gif


质感十足,适合用在一些需要对物联网智能设备进行控制的场景,比如调节某个智能音箱的音量。

稍加改造,也可用在一些类似交互的控件上

ecf305c0c1340f2218ae229a0c114744.png

一、设计思路

有时候,用户并不需要关心控件的具体读数,在一些具有读数的音量控制控件中,我们会发现一个有意思的现象:用户会特意将数值调至整数或偶数,当用户无法调至这个数时,则会焦虑感骤增,原地螺旋爆炸。如同吃完重庆火锅后往菊花里塞了颗薄荷糖——微辣中带一丝清凉。

那么,如果隐藏掉具体数值会不会更好呢?在一些老式音箱硬件上,我们会经常看到音量旋钮但不具备具体的读数,音量调至多少合适全凭入耳的感觉,感觉对了音量就对了。全程的交互中没有具体的读数概念,感觉是唯一的驱动力。

6e32b4a0ce5dd39eb7572aef8e5aa44e.png

二、实现方案

2.1 UI拆解

老套路,首先分析形状,不难发现,由旋钮、旋钮上的指示器、外围刻度构成

4576257606a0f94e9b3c072fe0c938d4.png

2.2 UI绘制

UI整体绘制难度非常简单,主要在ondraw中

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    setLayerType(LAYER_TYPE_SOFTWARE, null)

    canvas?.let {
        //刻度
        drawScale(it)
        //旋钮&阴影
        drawShadow(it)
        //指示器
        drawIndicator(it)
    }
}

2.2.1 绘制旋钮

绘制圆形的同时绘制阴影,增加质感

75e52c802fea5de71837b32f3285559c.png

代码如下

private fun drawShadow(canvas: Canvas) {
    paint.color = Color.WHITE
    paint.setShadowLayer(shadowSize, 0F, 15F, Color.GRAY)
    canvas.drawCircle(centerX, centerY, radius, paint)
    paint.clearShadowLayer()
}

2.2.2 绘制指示器

69919593141485059a1bf75648bd7c3c.png

代码如下,需要注意的是,指示器带有角度属性,需要旋转画布

private fun drawIndicator(canvas: Canvas) {
    canvas.save()
    canvas.rotate(circularOpUtils.curDegree, centerX, centerY)
    paint.color = Color.RED
    canvas.drawRect(RectF(centerX + radius / 4, centerY - 4, centerX + radius * 3 / 4, centerY + 4), paint)
    canvas.restore()
}

2.2.3 绘制刻度

8ab2e9b0d7069d155d8b746df9a9926d.png

代码如下:

private fun drawScale(canvas: Canvas) {
    Log.i(TAG, "curDegree==>" + circularOpUtils.curDegree)
    canvas.save()
    paint.color = Color.GRAY
    var scaleCount = 360 / scaleSpace.toInt()
    for (i in 0 until scaleCount) {
        //绘制当前指示
        if ((i * scaleSpace <= circularOpUtils.curDegree) && ((i + 1) * scaleSpace > circularOpUtils.curDegree)) {
            Log.i(TAG, "i*scaleSpace==>" + i * scaleSpace)
            paint.color = curSelScaleColor
            canvas.drawRect(width - scaleWidth, centerY - 4F, width - scaleWidth + curSelScaleWith, centerY + 4F, paint)
            paint.color = Color.GRAY
        } else {
            canvas.drawRect(width - scaleWidth, centerY - 4F, width.toFloat(), centerY + 4F, paint)
        }
        canvas.rotate(scaleSpace, centerX, centerY)
    }
    canvas.restore()
}

2.3 交互实现

按交互抽象出计算旋转角度的工具类CircularOpUtils,类似圆盘旋转的控件都可通用

2.3.1 旋转角度计算

思路是这样的,在手指移动中如何计算移动点和起始按压点的角度呢?可以采用几何公式,利用反tan或反cos;也可以采用两点分别与x轴的夹角的差进行计算。这里采用后者

/**
* 计算坐标点与x轴的夹角
*/
fun calculateAngle(x: Float, y: Float): Float {
    val distance = sqrt(((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY)))
    if (distance == 0F) {
        return 0F
    }
    var degree = acos((x - centerX) / distance) * 180 / PI.toFloat()
    if (y < centerY) {
        degree = 360 - degree
    }
    return degree
}


/**
* 计算两点的夹角
*/
fun calculateAngle(x1: Float, y1: Float, x2: Float, y2: Float): Float {
    val angle1 = calculateAngle(x1, y1)
    val angle2 = calculateAngle(x2, y2)
    return angle2 - angle1
}

2.3.2 刻度动画

主要是当前选中刻度的长短变化

private fun shrinkScaleAnim() {
    shrinkScaleAnim?.cancel()
    if (curSelScaleWith > 10F) {
        shrinkScaleAnim = ValueAnimator.ofFloat(curSelScaleWith, 10F)
        with(shrinkScaleAnim!!) {
            duration = 300L
            addUpdateListener {
                curSelScaleWith = it.animatedValue as Float
                postInvalidate()
            }
            start()
        }
    }
}

2.3.3 一点细节

可以观察到,用户手指按压和抬起时,旋钮的阴影变化

private fun startUpShadowAnim() {
    shadowAnim?.cancel()
    shadowAnim = ValueAnimator.ofFloat(shadowSize, 30F)
    with(shadowAnim!!) {
        duration = 300L
        addUpdateListener {
            shadowSize = animatedValue as Float
            postInvalidate()
        }
        start()
    }
}


private fun startDownShadowAnim() {
    shadowAnim?.cancel()
    shadowAnim = ValueAnimator.ofFloat(shadowSize, 20F)
    with(shadowAnim!!) {
        duration = 300L
        addUpdateListener {
            shadowSize = animatedValue as Float
            postInvalidate()
        }
        start()
    }
}

2.3.4 优化思路

这个控件涉及了刻度绘制,当没有动画在运行时,可以将刻度的形状使用path保存下来,用户交互时,旋转path即可,而不需要每次在ondraw中for循环生成刻度形状。

三、后记

如何减少用户在交互时的焦虑感?这是设计上可以探索的方向。

但是用户需要减少焦虑吗?现实中的应用都巴不得促进用户越来越焦虑。

看看吧

可以无限往下刷的信息列表;

可以无限刷的短视频;

可以无限下翻的回答、帖子;

无限次下拉刷新;

文字社区强行添加视频流;

腹泻式不停地兴趣推荐......

什么时候才是个头啊?

那种调节音箱音量旋钮时感觉对了就好了的体验不复存在了。

这样真的好吗?

我说,不好。

但我算个屁。

啊,既然看到了这里,是时候亮出我的个人微信和公众号了......才怪!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值