自定义View:Android 仿 PS 选色板

前言

今天突然发现年前的文章竟然没有写完,略微有点尴尬。今天分享的主题是Android仿PS选色板。

记得我刚开始学习Android的时候,就一直对PS选色板有一种执着,终于在今年找到了理想的解决方案。首先动图镇楼:


效果图

正文

我们常用的颜色主要是为ARGB还有RGB,不过这种组合格式想要实现选色板确实有点力不从心。从图上看,我们需要的二维矩阵,横向是饱和度的变化,纵向是明亮度的变化,类似我们电视机的色彩调节模式。ARGB和RGB是基本色的互相填充,想要精确把控颜色的变化还是很难的。

终于某一天我在网上偶然看到了新的颜色组合格式:HSV。

在这里插入图片描述

上面的简单介绍了HSV颜色的构成,非常符合选色板的要求,而且Android有系统API帮助我们把颜色在RGB和HSV之间互相转换。

// RGB转HSV
val hsv = FloatArray(3)
Color.HSVToColor(hsv)
hsv[0] // 色相
hsv[1] // 饱和度
hsv[2] // 明度
// HSV转RGB
val color = Color.HSVToColor(hsv)
// HSV转ARGB
val color = Color.HSVToColor(alpha, hsv)

绘制右侧的色相选择条

首先我们绘制右侧的色相选择条,把需要的色相按照均等渐变绘制一下:

private fun initLinearGradient() {
        positions = FloatArray(colorArray.size)
        for (i in colorArray.indices) {
            positions[i] = i * (1f / (colorArray.size - 1))
        }
        shader = LinearGradient(
            0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(),
            colorArray, positions, Shader.TileMode.CLAMP
        )
}

然后根据onTouchEvent的位置,计算手指按压的位置,到底是什么颜色,我这采取的方案是ArgbEvaluator:

 /**
     * 计算应该当前的颜色值
     * */
    private fun calculateCurrentColor(): Int {
        // 找到两个颜色区间
        if (progress == 0f) {
            return colorArray.first()
        }

        if (progress >= 1f) {
            return colorArray.last()
        }

        var startColor = 0
        var endColor = 0
        var ratio = 0f
        var i = positions.size - 1
        while (i >= 0) {
            if (progress >= positions[i]) {
                startColor = colorArray[i]
                endColor = colorArray[i + 1]
                ratio = (progress - positions[i]) / (positions[i + 1] - positions[i])
                break
            }
            i--
        }
        val argbEvaluator = ArgbEvaluator()
        return argbEvaluator.evaluate(ratio, startColor, endColor) as Int
    }

ArgbEvaluator只能计算两个颜色,所以我需要知道手指按压的位置处于哪两个颜色之间,刚才我们绘制渐变色的时候:

positions = FloatArray(colorArray.size)

已经记录了每一个颜色的位置,和手指的y坐标和View高度的比例是对应的,这样就可以具体颜色,不过颜色的格式RGB。

绘制左侧的渐变自定义View

从右侧的色相选择条得到了色相,第一步,把RGB转换为HSV,下面以横向饱和度为例:

Color.colorToHSV(it, hsv)
// 记录色相的饱和度
val temp = hsv[1]
// 保存饱和度为0的颜色
hsv[1] = 0f
val startColor = Color.HSVToColor(hsv)
// 再保存饱和度为1的颜色
hsv[1] = 1f
val endColor = Color.HSVToColor(hsv)
// 恢复之前的颜色
hsv[1] = temp
// 画出饱和度从0到1的渐变色
return LinearGradient(
          0f, 0f, width.toFloat(), 0f,
          intArrayOf(startColor, endColor),
          floatArrayOf(0f, 1f),
          Shader.TileMode.CLAMP
)

同理可以得到明度从0到1的渐变,然后两种渐变叠加在一起,我们的二维选择器就完成了。

HSV转RGB

接下来的问题是如何返回手指按下的颜色,这个和之前计算色相选择的道理一样,饱和度和明度的范围都是从0到1的,所以我们根据手指的x坐标和y坐标,求出坐标和宽高的比例,就是对应的饱和度的明度:

// 替换颜色饱和度和明度,返回RGB的颜色
private fun calculateCurrentColor(xDown: Float, yDown: Float): Int {
    val saturation = xDown / width
    val brightness = yDown / height
    hsv[1] = saturation
    hsv[2] = 1- brightness
    return Color.HSVToColor(hsv)
}

总结

其实选择器的核心思想就是RGB颜色和HSV颜色的互相转换,其他的都是逻辑问题。
具体demo请点击:选色板demo Github地址

你可能感兴趣的:(自定义View:Android 仿 PS 选色板)