본문 바로가기
Project/안드로이드 프로젝트(RandomColorChart)

Android Studio, Kotlin] 6. (Open Source) Holo Color Picker

by 김마리님 2021. 2. 17.

기존에 이 앱은 미리 만들어진 컬러를 추출하고, 이 컬러를 바탕으로 유사색, 보색 등에서 다양한 배리에이션을 찾아내기 위해 (+ 제가 UI 작업할 때 쓰려고) 만든 앱입니다. 그러니, 유사색과 보색을 찾기 위해서는 색을 뽑는데서 그치지 않고, 색의 배리에이션을 찾아내기 위해, HSV를 조절할 수 있어야 합니다. 그래서 찾아낸 오픈소스가 Holo Color Picker 입니다.

 

github.com/LarsWerkman/HoloColorPicker

 

LarsWerkman/HoloColorPicker

An Android Holo themed colorpicker designed by Marie Schweiz - LarsWerkman/HoloColorPicker

github.com

 

먼저 완성된 내용부터 보면..

 

(앗 gif로 만들다보니 좀 깨졌네여)

HSV에 따라 오른쪽 컬러가 변하는 것이 보입니다. 이번엔 이것을 써봅니다.

먼저 의존성을 부여합니다. 앱 수준에서 부여하면 됩니다.

 

dependencies {
...

    //Color Picker
    implementation 'com.larswerkman:HoloColorPicker:1.5'

...

}

 

다음 xml을 작성합니다.

 

Holo Picker에서는 제어할 수 있는 것이 총 네가지 입니다.

 

1. SV bar : 채도와 명도를 동시에 조절할 수 있습니다 => 휘도 조절

2. Opacity bar : 투명도를 조절할 수 있습니다.

3. Saturation bar : 채도를 조절할 수 있습니다.

4. Value bar : 명도를 조절할 수 있습니다.

 

저는 여기서 휘도와 투명도를 제외한, HSV만 조절할거라, 투명도바와 휘도 바를 과감히 삭제했습니다.

 

- activity_color_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".showdetail.ColorDetailActivity">

    <FrameLayout
        android:id="@+id/frameLayoutNavigation"
        android:layout_width="match_parent"
        android:layout_height="50dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginTop="15dp"
        android:layout_marginEnd="20dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/textViewColorName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Color Name : "
            android:textSize="16sp" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/textViewHEX"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="HEX : "
                android:textSize="16sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginStart="15dp"
                android:text="Copy"
                android:textSize="12sp"
                android:background="@drawable/copy_border"
                android:paddingTop="2dp"
                android:paddingBottom="2dp"
                android:paddingStart="5dp"
                android:paddingEnd="5dp"
                />

        </LinearLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="RGB : "
            android:textSize="16sp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/textViewRed"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Red : "
                android:textSize="14sp" />

            <TextView
                android:id="@+id/textViewGreen"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Green : "
                android:textSize="14sp" />

            <TextView
                android:id="@+id/textViewBlue"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Blue : "
                android:textSize="14sp" />


        </LinearLayout>


    </LinearLayout>


    <com.larswerkman.holocolorpicker.ColorPicker
        android:id="@+id/colorPicker"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="30dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="65dp"
        android:layout_marginEnd="60dp"
        android:layout_marginTop="10dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="채도 : " />

        <com.larswerkman.holocolorpicker.SaturationBar
            android:id="@+id/saturationBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="65dp"
        android:layout_marginEnd="60dp"
        android:layout_marginTop="10dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="명도 : " />

        <com.larswerkman.holocolorpicker.ValueBar
            android:id="@+id/valueBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginTop="15dp"
        android:layout_marginEnd="20dp"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Changed Color"
            android:textSize="16sp"/>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/textViewNewHEX"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="HEX : "
                android:textSize="16sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginStart="15dp"
                android:text="Copy"
                android:textSize="12sp"
                android:background="@drawable/copy_border"
                android:paddingTop="2dp"
                android:paddingBottom="2dp"
                android:paddingStart="5dp"
                android:paddingEnd="5dp"
                />

        </LinearLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="RGB : "
            android:textSize="16sp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/textViewNewRed"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Red : "
                android:textSize="14sp" />

            <TextView
                android:id="@+id/textViewNewGreen"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Green : "
                android:textSize="14sp" />

            <TextView
                android:id="@+id/textViewNewBlue"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Blue : "
                android:textSize="14sp" />


        </LinearLayout>


    </LinearLayout>


</LinearLayout>

 

다음 코틀린 파일에서 여기에 원하는 값을 디폴트로 부여하고, 조절하는것까지 해봅시다.

 

package com.mary.kotlinprojectstudy.showdetail

import android.graphics.Color
import android.icu.number.IntegerWidth
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.FrameLayout
import android.widget.TextView
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import com.larswerkman.holocolorpicker.*
import com.mary.kotlinprojectstudy.R
import com.mary.kotlinprojectstudy.bean.MainColor
import com.mary.kotlinprojectstudy.ui.NavigationViewHolder
import com.mary.kotlinprojectstudy.util.DlogUtil
import org.w3c.dom.Text
import java.util.*
import java.util.function.DoubleToLongFunction

class ColorDetailActivity : AppCompatActivity() {

    companion object {
        private const val TAG = "ColorDetailActivity"
    }

    private lateinit var textViewColorName: TextView
    private lateinit var textViewHEX: TextView
    private lateinit var textViewRed: TextView
    private lateinit var textViewGreen: TextView
    private lateinit var textViewBlue: TextView

    private lateinit var textViewNewHEX: TextView
    private lateinit var textViewNewRed: TextView
    private lateinit var textViewNewGreen: TextView
    private lateinit var textViewNewBlue: TextView

    private lateinit var frameLayoutNavigation: FrameLayout
    private lateinit var navigationViewHolder: NavigationViewHolder

    private val db = Firebase.firestore

    private lateinit var mainColor: MainColor

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_color_detail)

        checkBundle()
        findView()
        initNavigation()

    }

    private fun checkBundle() {
        var bundle = intent.getBundleExtra("BUNDLE_KEY")
        if (bundle != null) {
            DlogUtil.d(TAG, "번들 : ${bundle.getLong("id")}")
            loadColorById(bundle.getLong("id"))
        }
    }

    private fun findView() {
        frameLayoutNavigation = findViewById(R.id.frameLayoutNavigation)

        textViewColorName = findViewById(R.id.textViewColorName)
        textViewHEX = findViewById(R.id.textViewHEX)
        textViewRed = findViewById(R.id.textViewRed)
        textViewGreen = findViewById(R.id.textViewGreen)
        textViewBlue = findViewById(R.id.textViewBlue)

        textViewNewHEX = findViewById(R.id.textViewNewHEX)
        textViewNewRed = findViewById(R.id.textViewNewRed)
        textViewNewGreen = findViewById(R.id.textViewNewGreen)
        textViewNewBlue = findViewById(R.id.textViewNewBlue)
    }

    private fun initNavigation() {
        navigationViewHolder = NavigationViewHolder(this)
        navigationViewHolder.navigationViewHolderDelegate =
            object : NavigationViewHolder.NavigationViewHolderDelegate {
                override fun back() {
                    onBackPressed()
                }
            }

        navigationViewHolder.setTitle("Pick Color")
        frameLayoutNavigation.addView(navigationViewHolder.view)
    }

    private fun loadColorById(id: Long) {
        db.collection("colors").whereEqualTo("id", id).get()
            .addOnSuccessListener {
                run {
                    for (document in it) {
                        DlogUtil.d(TAG, "id 소환 성공. ${document.data}")
                        mainColor = document.toObject(MainColor::class.java)
                        initPicker(mainColor.red, mainColor.green, mainColor.blue)
                        updateView()
                    }
                }
            }
    }

    private fun updateView() {
        textViewColorName.text = "Color Name : ${mainColor.name} / ${mainColor.source}"
        textViewHEX.text = "HEX : #${mainColor.HEX}"
        textViewRed.text = "Red : ${mainColor.red}"
        textViewGreen.text = "Green : ${mainColor.green}"
        textViewBlue.text = "Blue : ${mainColor.blue}"

        textViewNewHEX.text = "HEX : #${mainColor.HEX}"
        textViewNewRed.text = "Red : ${mainColor.red}"
        textViewNewGreen.text = "Green : ${mainColor.green}"
        textViewNewBlue.text = "Blue : ${mainColor.blue}"
    }

    private fun newColorUpdateView(hex: String, red: Int, green: Int, blue: Int) {
        textViewNewHEX.text = "HEX : #$hex"
        textViewNewRed.text = "Red : $red"
        textViewNewGreen.text = "Green : $green"
        textViewNewBlue.text = "Blue : $blue"

    }

    private fun initPicker(red: Int, green: Int, blue: Int) {
        DlogUtil.d(TAG, "??? $red  $green  $blue")
        var picker: ColorPicker = findViewById(R.id.colorPicker)
        var saturationBar: SaturationBar = findViewById(R.id.saturationBar)
        var valueBar: ValueBar = findViewById(R.id.valueBar)

        picker.addSaturationBar(saturationBar)
        picker.addValueBar(valueBar)

        picker.color = Color.rgb(red, green, blue)
        picker.oldCenterColor = Color.rgb(red, green, blue)
        picker.setNewCenterColor(Color.rgb(red, green, blue))

        picker.setOnColorChangedListener {
            DlogUtil.d(TAG, "변경시... $it")
            var hexColor = Integer.toHexString(it)
            if (hexColor.length > 1) {
                hexColor = hexColor.substring(2)
            } else {
                DlogUtil.d(TAG, "뭐야 뭔값인데 ")
            }
            DlogUtil.d(TAG, "hex : $hexColor")

            newColorUpdateView(
                hexColor.toUpperCase(Locale.ROOT),
                Integer.parseInt(hexColor.substring(0, 2), 16),
                Integer.parseInt(hexColor.substring(2, 4), 16),
                Integer.parseInt(hexColor.substring(4, 6), 16)
            )

        }

    }


}

 

(DlogUtil은 logd 와 유사합니다.)

 

 

이 액티비티로 이동할때 해당 컬러의 id값을 받아옵니다. (check value)

이 아이디를 이용해 파이어스토어에서 값의 데이터베이스를 가져옵니다(loadColorById).

이 값을 가지고, 첫 UI를 갱신합니다. 이 때는 old color과 new color가 값이 같아야 하기 때문에 동시에 로드합니다(updateView).

 

이제 움직이는 컬러피커를 작업합니다. 먼저 디폴트 컬러(old Color)를 부여하기 위해 rgb 값을 인수로 먼저 가져옵니다.

먼저 피커와, 채명도 바를 먼저 선언합니다.

 

그리고 피커 내부에 채명도바를 추가시킵니다. 이제 채명도바를 조절해도 피커 내부의 new color이 변화하게 됩니다.

먼저 내부에 변하게 될 컬러(picker.color)와 디폴트 컬러(picker.oldCenterColor)을 같은 값으로 부여합니다.

이 때, picker.color을 부여하지 않으면 이후에 HSV바를 조절해도 변하지 않으니 유의해야합니다.

 

picker은 값이 변하는 리스너가 있습니다. 이 값을 통해서 변하는 값의 Hex와 RGB를 찾을 수 있습니다.

코틀린의 경우 리스너의 익명값으로 늘 it이 있으니, it 값을 찾아서 추출합니다.

먼저 이 값은 -6094957 같은, 알 수 없는 int 값으로 부여되어 있습니다. 이 값을 먼저 16진수로 변경합니다.

var hexColor = Integer.toHexString(it)

 

근데 이 값을, 오퍼시티도 합해진 8자리 RGBA 값을 도출합니다. 저희는 RGB만 필요하므로,, 값을 배열의 2번값 앞의 값을 잘라냅니다.

hexColor = hexColor.substring(2)

 

그러면 hex 값이 도출됩니다.

DlogUtil :: ColorDetailActivity.kt :: hex : a2ff93

 

이 값을 RGB로 변경시키는 법은 간단합니다. 

앞에서 1~2번째 값을 R, 3~4번째 값을 G, 5~6번째 값을 B로 상정하고 16진수 -> 10진수로 변환하기만 하면 됩니다. 

Integer.parseInt(hexColor.substring(0, 2), 16),
Integer.parseInt(hexColor.substring(2, 4), 16),
Integer.parseInt(hexColor.substring(4, 6), 16)

 

이제 이 값을 가지고, ChangedColorUpdated를 진행하면 됩니다. 이 때, 기존 값이 hex 값을 도출할 때, 전부 대문자로 출력하므로, 이 값도 upperCase를 통해 모두 대문자로 출력해줍시다.

 

newColorUpdateView(
hexColor.toUpperCase(Locale.ROOT),
Integer.parseInt(hexColor.substring(0, 2), 16),
Integer.parseInt(hexColor.substring(2, 4), 16),
Integer.parseInt(hexColor.substring(4, 6), 16)
)

 

private fun newColorUpdateView(hex: String, red: Int, green: Int, blue: Int) {
textViewNewHEX.text = "HEX : #$hex"
textViewNewRed.text = "Red : $red"
textViewNewGreen.text = "Green : $green"
textViewNewBlue.text = "Blue : $blue"
}

 

이렇게 만들면 동일하게 만들 수 있습니다.

반응형