193

Is there an easy way to use a custom image for a checkbox? I'm looking to duplicate the "starred" behavior of gmail. So I want to have a checkbox that, when checked, is a filled in star. And when unchecked is an empty star. Do I have to use an imageview and do my own logic myself?

Falmarri
  • 47,727
  • 41
  • 151
  • 191

12 Answers12

316

Create a drawable checkbox selector:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:drawable="@drawable/checkbox" 
          android:state_checked="false"/>
    <item android:drawable="@drawable/checkboxselected" 
          android:state_checked="true"/>
    <item android:drawable="@drawable/checkbox"/>    
</selector>

Make sure your checkbox is like this android:button="@drawable/checkbox_selector"

<CheckBox
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:button="@drawable/checkbox_selector"
    android:text="CheckBox"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:textColor="@color/Black" />
Bojan Dević
  • 1,875
  • 14
  • 24
129

Checkboxes being children of Button you can just give your checkbox a background image with several states as described here, under "Button style":

...and exemplified here:

Shashanth
  • 4,995
  • 7
  • 41
  • 51
Jean
  • 10,545
  • 2
  • 31
  • 31
  • 27
    Thanks, I actually found exactly what I needed here http://it-ride.blogspot.com/2010/04/how-to-android-favorite-button-right.html but I would have had to do it your way if I wanted a __real__ custom image =P – Falmarri Oct 19 '10 at 06:44
  • 2
    Thank you. Exactly what I've been looking for - I've figured out the whole state thing but I've been setting android:background instead of android:button and ended up with 2 buttons instead. Now it all works well. – Artem Russakovskii Nov 23 '10 at 23:07
  • 1
    -1. The `android:button` solution below is much better than using background attribute ! – Orabîg Sep 18 '12 at 20:05
  • 8
    @Orabîg: This downvote is wrong. The question is perfectly answered ("Custom checkbox image"). The fact that a shortcut exists for this particular starred-button does not invalidate this answer. – ereOn Oct 17 '12 at 15:00
  • While this maybe an old post, would like to add that Android studio also uses the android:button="@android:drawable/btn_star" method – Angry 84 Feb 04 '14 at 02:11
  • How to give ``width`` and ``height`` to the bitmap in the selector once you specify ``android:button``? – redcrow Aug 06 '15 at 15:28
  • Note that if you use the `button` attribute, it will not scale a background image. If you use a `background` attribute instead, and set `android:button=@null`, your background will scale to the size of the checkbox – karl Apr 13 '16 at 01:00
  • adding bare links is discouraged, please elaborate – Marian Paździoch Apr 13 '20 at 16:51
45

Copy the btn_check.xml from android-sdk/platforms/android-#/data/res/drawable to your project's drawable folder and change the 'on' and 'off' image states to your custom images.

Then your xml will just need android:button="@drawable/btn_check"

<CheckBox
    android:button="@drawable/btn_check"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:checked="true" />

If you want to use different default android icons, you can use android:button="@android:drawable/..."

WOUNDEDStevenJones
  • 5,150
  • 6
  • 41
  • 53
  • 2
    Bad advice. Icons may be changed from version to version and may disappear at all. If you really like the default icon, you can grab it from sources. – Korniltsev Anatoly Oct 13 '12 at 07:51
  • Are you saying referencing the default icons directly via "@android:drawable/..." is a bad idea, or this process entirely? – WOUNDEDStevenJones Oct 16 '12 at 19:42
  • 2
    Example: reference to holo icons will crash your app on pre-honeycomb devices. It is really difficult to maintain and debug such troubles. So I usually copy not only xml but the images too to be hundreed percent sure the resources will be found. Also this is very important to ensure UI look the same on every device. – Korniltsev Anatoly Oct 18 '12 at 06:47
27

res/drawable/day_selector.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android" >
        <item android:drawable="@drawable/dayselectionunselected"
              android:state_checked="false"/>
        <item android:drawable="@drawable/daysselectionselected"
              android:state_checked="true"/>
        <item android:drawable="@drawable/dayselectionunselected"/>
    </selector>

res/layout/my_layout.xml

<CheckBox
    android:id="@+id/check"
    android:layout_width="39dp"
    android:layout_height="39dp"
    android:background="@drawable/day_selector"
    android:button="@null"
    android:gravity="center"
    android:text="S"
    android:textColor="@color/black"
    android:textSize="12sp" />
Rahul
  • 481
  • 4
  • 9
6

If you have Android open source code, you can find the styles definition under:
src/frameworks/base/core/res/res/values

<style name="Widget.CompoundButton.CheckBox">
    <item name="android:background">
        @android:drawable/btn_check_label_background
    </item>
    <item name="android:button">
        ?android:attr/listChoiceIndicatorMultiple
    </item>
</style>
JJD
  • 50,076
  • 60
  • 203
  • 339
achellies
  • 986
  • 1
  • 7
  • 2
5

Based on the Enselic and Rahul answers.

It works for me (before and after API 21):

<CheckBox
    android:id="@+id/checkbox"
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:text=""
    android:gravity="center"

    android:background="@drawable/checkbox_selector"
    android:button="@null"
    app:buttonCompat="@null" />
alexrnov
  • 2,346
  • 3
  • 18
  • 34
3

Try it -

package com;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;



public class CheckBoxImageView extends ImageView implements View.OnClickListener {
    boolean checked;
    int defImageRes;
    int checkedImageRes;
    OnCheckedChangeListener onCheckedChangeListener;

    public CheckBoxImageView(Context context, AttributeSet attr, int defStyle) {
        super(context, attr, defStyle);
        init(attr, defStyle);
    }

    public CheckBoxImageView(Context context, AttributeSet attr) {
        super(context, attr);
        init(attr, -1);
    }

    public CheckBoxImageView(Context context) {
        super(context);
    }

    public boolean isChecked() {
        return checked;
    }

    public void setChecked(boolean checked) {
        this.checked = checked;
        setImageResource(checked ? checkedImageRes : defImageRes);
    }

    private void init(AttributeSet attributeSet, int defStyle) {
        TypedArray a = null;
        if (defStyle != -1)
            a = getContext().obtainStyledAttributes(attributeSet, R.styleable.CheckBoxImageView, defStyle, 0);
        else
            a = getContext().obtainStyledAttributes(attributeSet, R.styleable.CheckBoxImageView);
        defImageRes = a.getResourceId(0, 0);
        checkedImageRes = a.getResourceId(1, 0);
        checked = a.getBoolean(2, false);
        a.recycle();
        setImageResource(checked ? checkedImageRes : defImageRes);
        setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        checked = !checked;
        setImageResource(checked ? checkedImageRes : defImageRes);
        onCheckedChangeListener.onCheckedChanged(this, checked);
    }

    public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) {
        this.onCheckedChangeListener = onCheckedChangeListener;
    }

    public static interface OnCheckedChangeListener {
        void onCheckedChanged(View buttonView, boolean isChecked);
    }
}

Add this attrib -

<declare-styleable name="CheckBoxImageView">
        <attr name="default_img" format="integer"/>
        <attr name="checked_img" format="integer"/>
        <attr name="checked" format="boolean"/>
</declare-styleable>

Use like -

 <com.adonta.ziva.consumer.wrapper.CheckBoxImageView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/checkBox"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:clickable="true"
        android:padding="5dp"
        app:checked_img="@drawable/check_box_checked"
        app:default_img="@drawable/check_box" />

It will fix all your porblems.

Neo
  • 3,546
  • 1
  • 24
  • 31
  • It's missing the `onSaveInstanceState()` and `onRestoreInstanceState()` methods, I think the checked state would be lost on rotation – EpicPandaForce Dec 02 '15 at 13:23
2

Another option is to use a ToggleButton with null background and a custom button.

Bellow an example that includes a selector to the text color as well.

<ToggleButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:button="@drawable/toggle_selector"
    android:background="@null"
    android:paddingLeft="10dp"
    android:layout_centerHorizontal="true"
    android:gravity="center"
    android:textColor="@drawable/toggle_text"
    android:textOn="My on state"
    android:textOff="My off state" />

toggle_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:state_checked="true"
        android:drawable="@drawable/state_on" />

    <item
        android:drawable="@drawable/state_off" />

</selector>

toggle_text.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:state_checked="true"
        android:color="@color/app_color" />

    <item
        android:color="@android:color/darker_gray" />

</selector>
androidevil
  • 9,011
  • 14
  • 41
  • 79
2

If you are using custom adapters than android:focusable="false" and android:focusableInTouchMode="false" are nessesury to make list items clickable while using checkbox.

<CheckBox
        android:id="@+id/checkbox_fav"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:button="@drawable/checkbox_layout"/>

In drawable>checkbox_layout.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:drawable="@drawable/uncked_checkbox"
        android:state_checked="false"/>
    <item android:drawable="@drawable/selected_checkbox"
        android:state_checked="true"/>
    <item android:drawable="@drawable/uncked_checkbox"/>
</selector>
mehdi
  • 340
  • 4
  • 17
2

If you use androidx.appcompat:appcompat and want a custom drawable (of type selector with android:state_checked) to work on old platform versions in addition to new platform versions, you need to use

    <CheckBox
        app:buttonCompat="@drawable/..."

instead of

    <CheckBox
        android:button="@drawable/..."
Enselic
  • 4,434
  • 2
  • 30
  • 42
0

Adding custom drawable in the android:button did not work for me in Material Checkbox version-1.3.0 . I had to set android:drawable="@drawable/checkbox_selector" instead and also set android:button="@null" . You can also add android:drawablePadding to make it look good. However, this makes the entire checkbox clickable (along with the text).

Ali Akber
  • 492
  • 5
  • 9
0

All you need two drawable one for checked state and other for unchecked or default state. You can then create a background drawable selector(xml) and apply it as background to your checkbox. As mentioned below

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_checked="true"
        android:drawable="@drawable/ic_checkbox_checked_24" />
    <item
        android:state_checked="false"
        android:drawable="@drawable/ic_checkbox_unchecked_24" />
    <item
        android:drawable="@drawable/ic_checkbox_unchecked_24"/>
</selector>

No your checkbox in xml should look like this.

<CheckBox
      android:id="@+id/check_box"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:button="@null"
      android:background="@drawable/checkbox_background_drawable"/>

Make sure you have maked the button attribute of checkbox to null.

Wisal
  • 153
  • 2
  • 10