Android
CheckBox
UI Customization
Android Studio
Mobile Development

How to change the color of a CheckBox in Android

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Changing a CheckBox color in Android looks simple, but color often appears wrong when theme overlays, disabled state, and API differences are not handled together. A robust solution should define colors for checked, unchecked, and disabled states, then apply them in a way that survives activity restarts and theme changes. This guide shows a practical approach using XML tint resources and Kotlin code.

Understand What You Are Coloring

In Android, a CheckBox has two visual pieces: the box icon and the text label. Most color questions are about the box icon. If you only set text color, the check indicator can still use the default theme color, which makes the UI inconsistent.

The most reliable strategy is:

  • Use a stateful color resource for the indicator.
  • Apply it with android:buttonTint or app:buttonTint.
  • Keep label color separate using android:textColor.

When you treat these parts independently, your control remains readable in both light and dark themes.

Define a Stateful Color List in XML

A stateful color list keeps behavior predictable. The order of items matters, so put the most specific states first and the fallback last.

xml
1<!-- res/color/checkbox_tint.xml -->
2<selector xmlns:android="http://schemas.android.com/apk/res/android">
3    <item android:state_enabled="false" android:color="#9E9E9E" />
4    <item android:state_checked="true" android:color="#00796B" />
5    <item android:color="#455A64" />
6</selector>

Then use it in layout.

xml
1<androidx.appcompat.widget.AppCompatCheckBox
2    android:id="@+id/agreeCheck"
3    android:layout_width="wrap_content"
4    android:layout_height="wrap_content"
5    android:text="I agree to the policy"
6    android:textColor="#263238"
7    app:buttonTint="@color/checkbox_tint" />

Use AppCompatCheckBox when possible because support behavior is more consistent across Android versions.

Apply and Update Color Programmatically in Kotlin

If you need runtime theming or a user selected accent color, apply tint in Kotlin. The following example can run in an Activity and updates both initial state and dynamic changes.

kotlin
1import android.content.res.ColorStateList
2import android.os.Bundle
3import androidx.appcompat.app.AppCompatActivity
4import androidx.appcompat.widget.AppCompatCheckBox
5import androidx.core.content.ContextCompat
6import androidx.core.widget.CompoundButtonCompat
7
8class MainActivity : AppCompatActivity() {
9    override fun onCreate(savedInstanceState: Bundle?) {
10        super.onCreate(savedInstanceState)
11        setContentView(R.layout.activity_main)
12
13        val check = findViewById<AppCompatCheckBox>(R.id.agreeCheck)
14
15        val colors = intArrayOf(
16            ContextCompat.getColor(this, android.R.color.darker_gray),
17            ContextCompat.getColor(this, R.color.teal_700),
18            ContextCompat.getColor(this, R.color.blue_grey_700)
19        )
20        val states = arrayOf(
21            intArrayOf(-android.R.attr.state_enabled),
22            intArrayOf(android.R.attr.state_checked),
23            intArrayOf()
24        )
25
26        CompoundButtonCompat.setButtonTintList(check, ColorStateList(states, colors))
27
28        check.setOnCheckedChangeListener { _, isChecked ->
29            title = if (isChecked) "Accepted" else "Not accepted"
30        }
31    }
32}

This approach is useful when the color source comes from remote config or user preferences.

Support Older Devices with a Custom Button Drawable

On older projects, tint behavior can be inconsistent if theme setup is incomplete. A fallback is using a custom state list drawable assigned to android:button. That gives full control over checked and unchecked assets.

xml
1<!-- res/drawable/checkbox_button.xml -->
2<selector xmlns:android="http://schemas.android.com/apk/res/android">
3    <item android:state_checked="true" android:drawable="@drawable/ic_check_box_on" />
4    <item android:drawable="@drawable/ic_check_box_off" />
5</selector>
xml
1<CheckBox
2    android:id="@+id/legacyCheck"
3    android:layout_width="wrap_content"
4    android:layout_height="wrap_content"
5    android:button="@drawable/checkbox_button"
6    android:text="Legacy style" />

This method requires maintaining two drawable assets, but it is deterministic and easy to verify visually.

Common Pitfalls

  • Coloring only one state. If disabled and unchecked colors are missing, Android falls back to theme defaults and visual contrast can break.
  • Using android:buttonTint with plain CheckBox in a setup that expects app:buttonTint. Mixed widget families cause inconsistent rendering.
  • Forgetting dark theme testing. A color that looks clear in light mode can fail contrast checks in dark mode.
  • Setting tint in code before the view is fully initialized in complex custom views. Apply after view inflation or in lifecycle safe points.
  • Treating text and indicator as one style target. Keep them separate so accessibility remains strong.

Summary

  • Use a stateful color list to define checked, unchecked, and disabled behavior.
  • Prefer AppCompatCheckBox plus app:buttonTint for consistent cross version behavior.
  • Apply tint programmatically when theme data changes at runtime.
  • Use a custom button drawable fallback for strict legacy support.
  • Validate in light theme, dark theme, and disabled state before release.

Course illustration
Course illustration

All Rights Reserved.