How do I create ColorStateList programmatically?
Asked Answered
W

9

232

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?

Writer answered 21/3, 2013 at 9:4 Comment(0)
G
423

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)
Gratification answered 22/7, 2013 at 12:52 Comment(7)
This can be used change the color of a fab from the design library.Arteriovenous
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.)Incipient
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}, {}.Incipient
In case if someone will try any of those solutions, pay attention to the order the states like in selector.xml!Roden
contains no information what so ever regarding the two parametersTransducer
context.getResource().getColorStateList(R.color.sample_color_list_selector_resource, context.theme) color-list-resourceScolopendrid
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...Bully
P
145

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)
Plaque answered 6/6, 2017 at 3:26 Comment(1)
perfect wark around thx broEnvious
T
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)
        }
);
Trangtranquada answered 29/3, 2013 at 12:36 Comment(5)
Can you tell how to represent the below xml "<selector xmlns:android="schemas.android.com/apk/res/android" > <item android:state_pressed="true" android:color="@color/white"/> <item android:color="@color/black"/> </selector> " using colorstatelist.Klaus
@SatishKumar check my edit, I haven't tested it though.Trangtranquada
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}Strode
To add on to what @Strode 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.Incipient
... 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.!Incipient
B
40

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
        )
)
Butylene answered 19/2, 2019 at 16:23 Comment(1)
Also, see my answer below for a Kotlin helper function.Neumeyer
M
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];
            }
        }
    }
}
Merger answered 7/6, 2015 at 1:14 Comment(2)
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.Isometry
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?Incipient
N
28

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.

Neumeyer answered 26/9, 2019 at 16:46 Comment(0)
E
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);
Escritoire answered 15/1, 2018 at 6:45 Comment(0)
L
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);
Lovelorn answered 24/2, 2016 at 22:33 Comment(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 FragmentReshape
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.Incipient
H
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);
Horribly answered 9/5, 2023 at 18:23 Comment(1)
Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?Endanger

© 2022 - 2024 — McMap. All rights reserved.