¿Cómo creo ColorStateList mediante programación?

158

Estoy tratando de crear un ColorStateListprograma usando esto:

ColorStateList stateList = new ColorStateList(states, colors); 

Pero no estoy seguro de cuáles son los dos parámetros.

Según la documentación:

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

Agregado en API nivel 1

Crea una ColorStateList que devuelve la asignación especificada de estados a colores.

¿Alguien puede explicarme cómo crear esto?

¿Cuál es el significado de la matriz bidimensional para los estados?

Anukool
fuente

Respuestas:

344

Consulte http://developer.android.com/reference/android/R.attr.html#state_above_anchor para obtener una lista de los estados disponibles.

Si desea establecer colores para estados deshabilitados, desenfocados, desmarcados, etc., simplemente niegue los estados:

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
fuente
45
¡Gracias por la información sobre los estados "opuestos"!
BVB
Esto se puede usar para cambiar el color de un fab de la biblioteca de diseño.
Tapirboy
55
PRECAUCIÓN: Vea la respuesta de Roger Alien (y su primer comentario) para comprender que el orden de los estados aquí es pobre: ​​porque "habilitado" es primero, anulará otros estados que generalmente ocurren mientras un botón está habilitado. Es mejor poner "habilitado" al final. (O en lugar de "habilitado", un elemento vacío / predeterminado al final.)
ToolmakerSteve
2
Una lista básica de los estados de un botón que no retenga estado (no un conmutador / casilla) podría ser {pressed}, {focused}, {-enabled}, {}. Para una palanca que puede haber {checked, pressed}, {pressed}, {checked, focused}, {focused}, {checked}, {-enabled}, {}. O una palanca que hace caso omiso de enfoque: {checked, pressed}, {pressed}, {checked}, {-enabled}, {}.
ToolmakerSteve
En caso de que alguien pruebe alguna de esas soluciones, ¡preste atención al orden de los estados como en selector.xml!
Anton Makov el
75

La primera dimensión es una matriz de conjuntos de estados, la segunda es el conjunto de estados en sí. La matriz de colores enumera los colores para cada conjunto de estados coincidentes, por lo tanto, la longitud de la matriz de colores debe coincidir con la primera dimensión de la matriz de estados (o se bloqueará cuando el estado se "use"). Aquí y ejemplo:

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

espero que esto ayude.

EDITAR ejemplo: una lista de estados de color XML como:

<?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>

se vería así

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)
        }
);
Su-Au Hwang
fuente
¿Puedes decir cómo representar el siguiente 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> "usando colorstatelist.
Satish
@SatishKumar revisa mi edición, aunque no lo he probado.
Su-Au Hwang el
3
Vale la pena decir que para especificar un estado falso, puede negar su valor, por lo que si desea especificar un color para cuando no se presiona, debe usar: new int [] {- ​​android.R.attr.state_pressed}
tinsukE
1
Para agregar a lo que dijo @tinsukE: Sin embargo, para evitar suprimir accidentalmente un elemento más adelante en la lista, para la mayoría de los estados no tendrá sentido poner una negación; en su lugar, maneje todas las "otras" posibilidades con un elemento predeterminado (vacío) new int[]{}último: como se muestra en el bloque de código final de esta respuesta. El único valor negado que uso normalmente es "-habilitado". Otro ejemplo, si quieres tres colores diferentes: "enfocada + presionado", "centrado + no presionado", "presiona + No enfocado", usted puede poner simplemente {focused, pressed}, {focused}, {pressed}. Se utilizará el primer "verdadero".
ToolmakerSteve
2
... El error que podría hacer es tener una serie como {pressed}, {-pressed}, {focused}, {-focused}. El problema es que {pressed}y {-pressed}cubre TODAS las posibilidades (el botón se presiona o no se presiona), por lo que no se utilizarán los colores enumerados más adelante.
ToolmakerSteve
64

A veces esto será suficiente:

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

Lamentablemente, ninguna de las soluciones funciona para mí.

  1. Si no establece el estado presionado al principio, no lo detectará.
  2. Si lo configura, debe definir el estado vacío para agregar el color predeterminado
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
        }
);

Este es el constructor del código fuente:

/**
 * 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
fuente
55
Solo como una nota al margen: debe tratarlo como si fuera un if-elseif. Selecciona el primer estado que es verdadero. Por lo tanto, si tiene el estado habilitado como primer estado, se seleccionará antes del estado_presionado, a menos que la vista esté deshabilitada.
LeoFarage
FWIW, dado que tienes un elemento predeterminado al final, no creo que el primer elemento "habilitado" te esté haciendo ningún bien. ¿Por qué no eliminarlo por completo?
ToolmakerSteve
18

Aquí hay un ejemplo de cómo crear un ColorListprograma en 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
fuente
Además, vea mi respuesta a continuación para una función auxiliar de Kotlin.
arekolek
7

Rechazando la respuesta de Jonathan Ellis , en Kotlin puede definir una función auxiliar para hacer que el código sea un poco más idiomático y más fácil de leer, por lo que puede escribir esto en su lugar:

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

colorStateListOf puede implementarse así:

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

Tambien tengo:

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

Para poder llamar al mismo nombre de función, sin importar si es un selector o un solo color.

arekolek
fuente
3

Mi clase de constructor para crear 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;
    }
}

Ejemplo de uso

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);
Phan Van Linh
fuente
2

si usa el recurso 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
fuente
2
como getResources()está en desuso, es ahora ContextCompat.getColor(this,R.color.colorname);o ContextCompat.getColor(getActivity(),R.color.colorname);para uso en un Fragmento
iBobb
Para aclarar para otros lectores, new int[0](como un elemento en la lista del primer parámetro) es una matriz de longitud cero y representa la configuración del color predeterminado. Aquí es el único elemento, lo que significa que el tinte se aplica a todos los estados del botón. Esto es equivalente a lo new int[]{}visto en la respuesta de Roger Alien.
ToolmakerSteve