208

I am trying to create a ColorStateList programatically using this:

ColorStateList stateList = new ColorStateList(states, colors); 

But I am not sure what are the two parameters.

As per the documentation:

public ColorStateList (int[][] states, int[] colors) 

Added in API level 1

Creates a ColorStateList that returns the specified mapping from states to colors.

Can somebody please explain me how to create this?

What is the meaning of two-dimensional array for states?

Sufian
  • 6,225
  • 16
  • 65
  • 116
Anukool
  • 5,091
  • 8
  • 27
  • 41

8 Answers8

405

See http://developer.android.com/reference/android/R.attr.html#state_above_anchor for a list of available states.

If you want to set colors for disabled, unfocused, unchecked states etc. just negate the states:

int[][] states = new int[][] {
    new int[] { android.R.attr.state_enabled}, // enabled
    new int[] {-android.R.attr.state_enabled}, // disabled
    new int[] {-android.R.attr.state_checked}, // unchecked
    new int[] { android.R.attr.state_pressed}  // pressed
};

int[] colors = new int[] {
    Color.BLACK,
    Color.RED,
    Color.GREEN,
    Color.BLUE
};

ColorStateList myList = new ColorStateList(states, colors);
Caner
  • 53,818
  • 35
  • 164
  • 173
  • 55
    Thanks for the info regarding "opposite" states! – BVB Jan 07 '14 at 21:11
  • This can be used change the color of a fab from the design library. – J.G.Sebring Aug 14 '15 at 10:43
  • 7
    CAUTION: See Roger Alien's answer (and its first comment) to understand that the order of states here is poor: because "enabled" is first, it will override other states that typically occur while a button is enabled. Better to put "enabled" last. (Or instead of "enabled", an empty/default item last.) – ToolmakerSteve Dec 16 '16 at 21:04
  • 2
    A basic list of states for a button that does not retain state (NOT a toggle/checkbox) might be `{pressed}`, `{focused}`, `{-enabled}`, `{}`. For a toggle it might be `{checked, pressed}`, `{pressed}`, `{checked, focused}`, `{focused}`, `{checked}`, `{-enabled}`, `{}`. Or a toggle that ignores focus: `{checked, pressed}`, `{pressed}`, `{checked}`, `{-enabled}`, `{}`. – ToolmakerSteve Dec 16 '16 at 21:44
  • 1
    In case if someone will try any of those solutions, pay attention to the order the states like in selector.xml! – Anton Makov Oct 04 '19 at 12:34
  • contains no information what so ever regarding the two parameters – mightyWOZ May 07 '20 at 08:32
  • `context.getResource().getColorStateList(R.color.sample_color_list_selector_resource, context.theme)` [color-list-resource](https://developer.android.com/guide/topics/resources/color-list-resource) – Abhinav Atul Jul 18 '20 at 14:16
  • Absolutely hideous API design. – Jelle Blaauw Feb 22 '21 at 16:54
119

Sometimes this will be enough:

int colorInt = getResources().getColor(R.color.ColorVerificaLunes);
ColorStateList csl = ColorStateList.valueOf(colorInt);
tse
  • 5,387
  • 5
  • 36
  • 56
90

The first dimension is an array of state sets, the second ist the state set itself. The colors array lists the colors for each matching state set, therefore the length of the colors array has to match the first dimension of the states array (or it will crash when the state is "used"). Here and example:

ColorStateList myColorStateList = new ColorStateList(
                        new int[][]{
                                new int[]{android.R.attr.state_pressed}, //1
                                new int[]{android.R.attr.state_focused}, //2
                                new int[]{android.R.attr.state_focused, android.R.attr.state_pressed} //3
                        },
                        new int[] {
                            Color.RED, //1
                            Color.GREEN, //2
                            Color.BLUE //3
                        }
                    );

hope this helps.

EDIT example: a xml color state list like:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:color="@color/white"/>
    <item android:color="@color/black"/>
</selector>

would look like this

ColorStateList myColorStateList = new ColorStateList(
        new int[][]{
                new int[]{android.R.attr.state_pressed},
                new int[]{}
        },
        new int[] {
                context.getResources().getColor(R.color.white),
                context.getResources().getColor(R.color.black)
        }
);
Lorne Laliberte
  • 6,111
  • 2
  • 34
  • 36
Su-Au Hwang
  • 4,416
  • 19
  • 25
  • Can you tell how to represent the below xml " " using colorstatelist. – Satish Nov 03 '14 at 08:09
  • @SatishKumar check my edit, I haven't tested it though. – Su-Au Hwang Nov 03 '14 at 10:55
  • 3
    Worth saying that to specify a false state, you can negate its value, so if you want to specify a color for when it is not pressed, you should use: new int[]{-android.R.attr.state_pressed} – tinsukE Jan 07 '15 at 09:19
  • 1
    To add on to what @tinsukE said: However, to avoid accidentally suppressing an item later in the list, for most states it won't make sense to put a negation - instead handle all "other" possibilities with a default (empty) item `new int[]{}` last -- as shown in the final code block of this answer. The only negated value I typically use is "-enabled". Another example, if you want three different colors: "focused + pressed", "focused + not pressed", "pressed + not focused", you can simply put `{focused, pressed}`, `{focused}`, `{pressed}`. The first "true" one will be used. – ToolmakerSteve Dec 16 '16 at 21:20
  • 2
    ... The mistake you might make is to have a series like `{pressed}`, `{-pressed}`, `{focused}`, `{-focused`}. The problem is that `{pressed}` and `{-pressed}` cover ALL possibilities (the button is either pressed or not pressed), so no colors listed later will ever be used.! – ToolmakerSteve Dec 16 '16 at 21:25
33

Here's an example of how to create a ColorList programmatically in Kotlin:

val colorList = ColorStateList(
        arrayOf(
                intArrayOf(-android.R.attr.state_enabled),  // Disabled
                intArrayOf(android.R.attr.state_enabled)    // Enabled
        ),
        intArrayOf(
                Color.BLACK,     // The color for the Disabled state
                Color.RED        // The color for the Enabled state
        )
)
Jonathan Ellis
  • 5,021
  • 2
  • 34
  • 51
  • Also, see [my answer below](https://stackoverflow.com/a/58121371/1916449) for a Kotlin helper function. – arekolek Sep 26 '19 at 16:48
29

Unfortunately none of the solutions works for me.

  1. If you don't set pressed state at first it won't detect it.
  2. If you set it, then you need to define empty state to add default color
ColorStateList themeColorStateList = new ColorStateList(
        new int[][]{
                new int[]{android.R.attr.state_pressed},
                new int[]{android.R.attr.state_enabled},
                new int[]{android.R.attr.state_focused, android.R.attr.state_pressed},
                new int[]{-android.R.attr.state_enabled},
                new int[]{} // this should be empty to make default color as we want
        },
        new int[]{
                pressedFontColor,
                defaultFontColor,
                pressedFontColor,
                disabledFontColor,
                defaultFontColor
        }
);

This is constructor from source code:

/**
 * Creates a ColorStateList that returns the specified mapping from
 * states to colors.
 */
public ColorStateList(int[][] states, int[] colors) {
    mStateSpecs = states;
    mColors = colors;

    if (states.length > 0) {
        mDefaultColor = colors[0];

        for (int i = 0; i < states.length; i++) {
            if (states[i].length == 0) {
                mDefaultColor = colors[i];
            }
        }
    }
}
Roger Alien
  • 2,972
  • 1
  • 36
  • 46
  • 7
    Just as a sidenote: You have to treat it as you would an if-elseif. It selects the first state that is true. So, if you have the state_enabled as first state, it will be selected before the state_pressed -- unless the view is disabled. – LeoFarage Jun 07 '16 at 21:50
  • FWIW, since you've got a default element last, I don't think the first "enabled" element is doing you any good at all. Why not remove it completely? – ToolmakerSteve Dec 16 '16 at 21:06
24

Bouncing off the answer by Jonathan Ellis, in Kotlin you can define a helper function to make the code a bit more idiomatic and easier to read, so you can write this instead:

val colorList = colorStateListOf(
    intArrayOf(-android.R.attr.state_enabled) to Color.BLACK,
    intArrayOf(android.R.attr.state_enabled) to Color.RED,
)

colorStateListOf can be implemented like this:

fun colorStateListOf(vararg mapping: Pair<IntArray, Int>): ColorStateList {
    val (states, colors) = mapping.unzip()
    return ColorStateList(states.toTypedArray(), colors.toIntArray())
}

I also have:

fun colorStateListOf(@ColorInt color: Int): ColorStateList {
    return ColorStateList.valueOf(color)
}

So that I can call the same function name, no matter if it's a selector or single color.

arekolek
  • 8,187
  • 3
  • 55
  • 75
3

My builder class for create ColorStateList

private class ColorStateListBuilder {
    List<Integer> colors = new ArrayList<>();
    List<int[]> states = new ArrayList<>();

    public ColorStateListBuilder addState(int[] state, int color) {
        states.add(state);
        colors.add(color);
        return this;
    }

    public ColorStateList build() {
        return new ColorStateList(convertToTwoDimensionalIntArray(states),
                convertToIntArray(colors));
    }

    private int[][] convertToTwoDimensionalIntArray(List<int[]> integers) {
        int[][] result = new int[integers.size()][1];
        Iterator<int[]> iterator = integers.iterator();
        for (int i = 0; iterator.hasNext(); i++) {
            result[i] = iterator.next();
        }
        return result;
    }

    private int[] convertToIntArray(List<Integer> integers) {
        int[] result = new int[integers.size()];
        Iterator<Integer> iterator = integers.iterator();
        for (int i = 0; iterator.hasNext(); i++) {
            result[i] = iterator.next();
        }
        return result;
    }
}

Example Using

ColorStateListBuilder builder = new ColorStateListBuilder();
builder.addState(new int[] { android.R.attr.state_pressed }, ContextCompat.getColor(this, colorRes))
       .addState(new int[] { android.R.attr.state_selected }, Color.GREEN)
       .addState(..., some color);

if(// some condition){
      builder.addState(..., some color);
}
builder.addState(new int[] {}, colorNormal); // must add default state at last of all state

ColorStateList stateList = builder.build(); // ColorStateList created here

// textView.setTextColor(stateList);
Linh
  • 51,033
  • 19
  • 228
  • 256
2

if you use the resource the Colors.xml

int[] colors = new int[] {
                getResources().getColor(R.color.ColorVerificaLunes),
                getResources().getColor(R.color.ColorVerificaMartes),
                getResources().getColor(R.color.ColorVerificaMiercoles),
                getResources().getColor(R.color.ColorVerificaJueves),
                getResources().getColor(R.color.ColorVerificaViernes)

        };

ColorStateList csl = new ColorStateList(new int[][]{new int[0]}, new int[]{colors[0]}); 

    example.setBackgroundTintList(csl);
David Hackro
  • 3,656
  • 6
  • 39
  • 60
  • 2
    as `getResources()` is deprecated, it is now `ContextCompat.getColor(this,R.color.colorname);` or `ContextCompat.getColor(getActivity(),R.color.colorname);` for usage in a Fragment – iBobb Mar 28 '16 at 23:06
  • To clarify for other readers, `new int[0]` (as an element in the first parameter's list) is a zero-length array, and represents setting the default color. Here it is the only element, which means the tint is applied to *all* states of the button. This is equivalent to `new int[]{}` seen in Roger Alien's answer. – ToolmakerSteve Dec 16 '16 at 20:56