228

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,405
  • 16
  • 66
  • 120
Anukool
  • 5,301
  • 8
  • 29
  • 41

9 Answers9

420

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);

Kotlin:

    val states = arrayOf(
        intArrayOf(android.R.attr.state_enabled), // enabled
        intArrayOf(-android.R.attr.state_enabled), // disabled
        intArrayOf(-android.R.attr.state_checked), // unchecked
        intArrayOf(android.R.attr.state_pressed)  // pressed
    )

    val colors = intArrayOf(
        Color.BLACK,
        Color.RED,
        Color.GREEN,
        Color.BLUE
    )

    val myList = ColorStateList(states, colors)
Muhamed El-Banna
  • 593
  • 7
  • 21
Caner
  • 57,267
  • 35
  • 174
  • 180
  • This can be used change the color of a fab from the design library. – J.G.Sebring Aug 14 '15 at 10:43
  • 9
    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
  • 3
    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
  • 2
    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
  • What if I want to set a tint color for just one state, and let the rest stay with their default (no tinting) ? For some reason when I do this, I get it fully transparent for the non-tinted states... – android developer Mar 16 '23 at 13:39
138

Sometimes this would be enough:

int colorInt = getResources().getColor(R.color.ColorVerificaLunes);
ColorStateList csl = ColorStateList.valueOf(colorInt);

Kotlin:

val colorInt: Int = context.getColor(R.color.ColorVerificaLunes)
val csl = ColorStateList.valueOf(colorInt)
tse
  • 5,769
  • 6
  • 38
  • 58
95

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
    }
);

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)
        }
);
starball
  • 20,030
  • 7
  • 43
  • 238
Suau
  • 4,628
  • 22
  • 28
  • 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. – Suau 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
39

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,221
  • 2
  • 36
  • 53
  • Also, see [my answer below](https://stackoverflow.com/a/58121371/1916449) for a Kotlin helper function. – arekolek Sep 26 '19 at 16:48
30

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
  • 3,040
  • 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
27

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
  • 9,128
  • 3
  • 58
  • 79
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
  • 57,942
  • 23
  • 262
  • 279
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,652
  • 6
  • 41
  • 61
  • 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
0

Here for checked and unchecked

   int[][] states = new int[][] {
            new int[] {android.R.attr.state_checked}, //checked  
            new int[] { -android.R.attr.state_checked} // unchecked

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

    ColorStateList stateList = new ColorStateList(states, colors);