自定義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地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章