Wie erstelle ich ColorStateList programmgesteuert?


157

Ich versuche ein ColorStateListprogrammgesteuertes Erstellen mit diesem:

ColorStateList stateList = new ColorStateList(states, colors); 

Ich bin mir aber nicht sicher, was die beiden Parameter sind.

Gemäß Dokumentation:

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

In API-Ebene 1 hinzugefügt

Erstellt eine ColorStateList, die die angegebene Zuordnung von Status zu Farben zurückgibt.

Kann mir bitte jemand erklären, wie man das erstellt?

Was bedeutet zweidimensionales Array für Zustände?

Antworten:


341

Eine Liste der verfügbaren Status finden Sie unter http://developer.android.com/reference/android/R.attr.html#state_above_anchor .

Wenn Sie Farben für deaktivierte, nicht fokussierte, nicht aktivierte Zustände usw. festlegen möchten, negieren Sie einfach die Zustände:

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

45
Danke für die Info zu "entgegengesetzten" Staaten!
BVB

Dies kann verwendet werden, um die Farbe einer Fabrik aus der Designbibliothek zu ändern.
Tapirboy

5
VORSICHT: Lesen Sie die Antwort von Roger Alien (und seinen ersten Kommentar), um zu verstehen, dass die Reihenfolge der Zustände hier schlecht ist: Da "aktiviert" an erster Stelle steht, werden andere Zustände überschrieben, die normalerweise auftreten, wenn eine Schaltfläche aktiviert ist. Besser "aktiviert" zuletzt setzen. (Oder anstelle von "aktiviert" ein leeres / Standardelement zuletzt.)
ToolmakerSteve

2
Eine grundlegende Liste der Staaten für eine Schaltfläche , den Zustand nicht beibehalten (NICHT ein Toggle / Checkbox) könnte sein {pressed}, {focused}, {-enabled}, {}. Für eine Umschaltfunktion , es könnte sein {checked, pressed}, {pressed}, {checked, focused}, {focused}, {checked}, {-enabled}, {}. Oder ein Knebel , dass ignoriert konzentrieren: {checked, pressed}, {pressed}, {checked}, {-enabled}, {}.
ToolmakerSteve

Falls jemand eine dieser Lösungen ausprobieren sollte, achten Sie auf die Reihenfolge der Zustände in selector.xml!
Anton Makov

75

Die erste Dimension ist ein Array von Statussätzen, die zweite ist der Statussatz selbst. Das Farbarray listet die Farben für jeden übereinstimmenden Statussatz auf, daher muss die Länge des Farbarrays mit der ersten Dimension des Statusarrays übereinstimmen (oder es stürzt ab, wenn der Status "verwendet" wird). Hier und Beispiel:

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

hoffe das hilft.

BEARBEITEN Beispiel: Eine XML-Farbstatusliste wie:

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

würde so aussehen

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

Können Sie sagen, wie die folgende XML dargestellt werden soll ? <Selector xmlns: android = " schemas.android.com/apk/res/android "> <item android: state_pressed = "true" android: color = "@ color / white" / > <item android: color = "@ color / black" /> </ select> "mit colorstatelist.
Satish

@ SatishKumar überprüfe meine Bearbeitung, ich habe sie jedoch nicht getestet.
Su-Au Hwang

3
Um einen falschen Status anzugeben, können Sie dessen Wert negieren. Wenn Sie also eine Farbe angeben möchten, wenn diese nicht gedrückt wird, sollten Sie Folgendes verwenden: new int [] {- ​​android.R.attr.state_pressed}
tinsukE

1
Um das zu ergänzen, was @tinsukE gesagt hat: Um jedoch zu vermeiden, dass ein Element später in der Liste versehentlich unterdrückt wird, ist es für die meisten Staaten nicht sinnvoll, eine Negation einzufügen. Behandeln Sie stattdessen alle "anderen" Möglichkeiten mit einem Standardelement (leer) new int[]{}last - wie im letzten Codeblock dieser Antwort gezeigt. Der einzige negierte Wert, den ich normalerweise verwende, ist "-enabled". Ein weiteres Beispiel : Wenn Sie drei verschiedene Farben wünschen: „fokussiert + gedrückt“, „fokussiert + nicht gedrückt“, „gedrückt + nicht fokussiert“, können Sie einfach gesagt {focused, pressed}, {focused}, {pressed}. Das erste "wahre" wird verwendet.
ToolmakerSteve

2
... Der Fehler , den man machen könnte , ist eine Reihe zu haben , wie {pressed}, {-pressed}, {focused}, {-focused}. Das Problem ist , dass {pressed}und {-pressed}alle Möglichkeiten abdecken (die Taste entweder gedrückt oder nicht gedrückt), so dass keine Farben später aufgeführt werden jemals verwendet werden.!
ToolmakerSteve

64

Manchmal reicht das aus:

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

20

Leider funktioniert keine der Lösungen für mich.

  1. Wenn Sie den gedrückten Zustand zuerst nicht einstellen, wird er nicht erkannt.
  2. Wenn Sie es festlegen, müssen Sie den leeren Status definieren, um die Standardfarbe hinzuzufügen
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
        }
);

Dies ist der Konstruktor aus dem Quellcode:

/**
 * 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];
            }
        }
    }
}

5
Nur als Randnotiz: Sie müssen es wie ein Wenn-Sonst behandeln. Es wählt den ersten Zustand aus, der wahr ist. Wenn Sie also state_enabled als ersten Status haben, wird dieser vor state_pressed ausgewählt - es sei denn, die Ansicht ist deaktiviert.
LeoFarage

FWIW, da Sie zuletzt ein Standardelement haben, glaube ich nicht, dass das erste "aktivierte" Element Ihnen überhaupt etwas nützt. Warum nicht komplett entfernen?
ToolmakerSteve

18

Hier ist ein Beispiel für das ColorListprogrammgesteuerte Erstellen eines 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
        )
)

Siehe auch meine Antwort unten für eine Kotlin-Hilfsfunktion.
Arekolek

7

In Anlehnung an die Antwort von Jonathan Ellis können Sie in Kotlin eine Hilfsfunktion definieren, um den Code etwas idiomatischer und leichter lesbar zu machen. Sie können dies stattdessen schreiben:

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

colorStateListOf kann wie folgt implementiert werden:

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

Ich habe auch:

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

Damit ich den gleichen Funktionsnamen aufrufen kann, egal ob es sich um einen Selektor oder eine einzelne Farbe handelt.


3

Meine Builder-Klasse zum Erstellen 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;
    }
}

Beispiel mit

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

2

Wenn Sie die Ressource Colors.xml verwenden

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

2
wie getResources()veraltet, ist es jetzt ContextCompat.getColor(this,R.color.colorname);oder ContextCompat.getColor(getActivity(),R.color.colorname);zur Verwendung in einem Fragment
iBobb

Zur Verdeutlichung für andere Leser new int[0](als Element in der Liste des ersten Parameters) handelt es sich um ein Array mit der Länge Null, das die Einstellung der Standardfarbe darstellt. Hier ist es das einzige Element, was bedeutet, dass der Farbton auf alle Zustände der Schaltfläche angewendet wird . Dies entspricht new int[]{}der Antwort von Roger Alien.
ToolmakerSteve
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.