前言
今天突然發現年前的文章竟然沒有寫完,略微有點尷尬。今天分享的主題是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地址