Logo
Published on
·7 min read

Using the Color Picker (ambilwarna) library to change colors (Android).

I apologize in advance for any awkward expressions in English.

English is not my native language, and I have relied on ChatGPT's assistance to proceed with the translation.

Overview

I've added the functionality for users to customize background and text colors in the sosone project's Smart Desk Clock (Android application).

The color selection feature utilizes the Android Color Picker (ambilwarna) library.

Service Shortcut

Smart Desk Clock

Screen Preview

The operating screen looks like the image below.

When you click the color button on the left, the color selection dialog on the right is displayed.

Color Picker Screen

Brief Overview of the Work

Adding the Library to build.gradle (Module)

implementation 'com.github.yukuku:ambilwarna:2.0.1'

Layout Section

  • Adding a color button

Kotlin Code Section

  • Calling the color selection dialog when the button is clicked and setting a listener for color selection

layout

fragment_setting.xml

  • This is the part of the settings layout that displays the color button.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ... omitted ...>

	<!-- omitted -->

    <LinearLayout
        android:id="@+id/view_custom_colors"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:visibility="gone"
        android:baselineAligned="false"
        >
        <LinearLayout
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:layout_marginEnd="2dp"
            android:orientation="vertical">
            <com.google.android.material.textview.MaterialTextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/background_color" />
            <com.google.android.material.button.MaterialButton
                android:id="@+id/btn_custom_background_color"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:backgroundTint="@color/default_background_color"
                app:strokeColor="@color/teal_700"
                app:strokeWidth="4dp" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:layout_marginStart="2dp"
            android:orientation="vertical">
            <com.google.android.material.textview.MaterialTextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/text_color" />
            <com.google.android.material.button.MaterialButton
                android:id="@+id/btn_custom_text_color"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:backgroundTint="@color/default_text_color"
                app:strokeColor="@color/teal_700"
                app:strokeWidth="4dp" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

Kotlin Source (Fragment)

SettingFragment.kt

Please refer to the comments in the code.

package kr.sosone.clock

import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import kr.sosone.clock.common.EnumFont
import kr.sosone.clock.common.Util
import kr.sosone.clock.common.Util.Companion.marginPixel
import kr.sosone.clock.common.Util.Companion.presetSize
import kr.sosone.clock.databinding.FragmentSettingBinding
import kr.sosone.clock.viewmodels.ClockViewModel
import kr.sosone.clock.viewmodels.ClockViewModelFactory
import yuku.ambilwarna.AmbilWarnaDialog

class SettingFragment : Fragment() {

    private var _binding : FragmentSettingBinding? = null
    private val binding get() = _binding!!

    // The data that has been set is managed by the view model.
    private val viewModel: ClockViewModel by activityViewModels{
        ClockViewModelFactory(
            (activity?.application as ClockApplication).database.settingDao()
        )
    }

    // ...omitted...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Specify the button color.
        // Since the stored color is a String, convert it to an Int using Color.parseColor.
        binding.btnCustomBackgroundColor.setBackgroundColor(Color.parseColor(viewModel.customBackgroundColor.value))
        binding.btnCustomTextColor.setBackgroundColor(Color.parseColor(viewModel.customTextColor.value))

		// When the color button is clicked, the loadColorPicker function is called.
        binding.btnCustomBackgroundColor.setOnClickListener {
            loadColorPicker(viewModel.customBackgroundColor.value ?: Util.defaultBackgroundColor, true)
        }

        // When the color button is clicked, the loadColorPicker function is called.
        binding.btnCustomTextColor.setOnClickListener {
            loadColorPicker(viewModel.customTextColor.value ?: Util.defaultTextColor, false)
        }
    }

    // This is the part where the Color Picker library is invoked.
    private fun loadColorPicker(defaultColor: String, isBackgroundColor: Boolean){
        val color = Color.parseColor(defaultColor)
        AmbilWarnaDialog(requireActivity(), color,
            object : AmbilWarnaDialog.OnAmbilWarnaListener {
                override fun onCancel(dialog: AmbilWarnaDialog?) {

                }

                // Handling the color change
                override fun onOk(dialog: AmbilWarnaDialog?, color: Int) {
                	// Code for converting an Int color to a String for storage in ViewModel
                    val hexColor = String.format("#%06X", 0xFFFFFF and color)
                    if(isBackgroundColor){
                        viewModel.changeCustomBackgroundColor(hexColor)
                        binding.btnCustomBackgroundColor.setBackgroundColor(color)
                    }else{
                        viewModel.changeCustomTextColor(hexColor)
                        binding.btnCustomTextColor.setBackgroundColor(color)
                    }

                }
            }).show()
    }
}

For more detailed information about the library, please refer to the yukuku/ambilwarna GitHub repository.

Additional Note

When choosing a library for color picking, I initially considered using ColorPickerView. It appears to have active updates and offers a good design, as well as straightforward usage.

However, I encountered issues with the dialog size not matching after rotation, so I had to reconsider it. If you can customize it or if you don't need rotation support, that library looks good as well.

The AmbilWarna library I used seems to have had its last update quite a while ago as of 2023. However, it worked well with my project using compileSdk 33 and targetSdk 33. While the design and updates might not be the best, it didn't have any functional issues, which is why I chose it.