前言
今天突然发现年前的文章竟然没有写完,略微有点尴尬。今天分享的主题是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地址