Jak programowo utworzyć ColorStateList?


158

Próbuję stworzyć ColorStateListprogramowo używając tego:

ColorStateList stateList = new ColorStateList(states, colors); 

Ale nie jestem pewien, jakie są te dwa parametry.

Zgodnie z dokumentacją:

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

Dodano na poziomie API 1

Tworzy ColorStateList, który zwraca określone mapowanie ze stanów na kolory.

Czy ktoś może mi wyjaśnić, jak to stworzyć?

Jakie jest znaczenie dwuwymiarowej tablicy stanów?

Odpowiedzi:


343

Zobacz http://developer.android.com/reference/android/R.attr.html#state_above_anchor, aby zapoznać się z listą dostępnych stanów.

Jeśli chcesz ustawić kolory dla stanów wyłączonych, nieostre, niezaznaczonych itp., Po prostu zaneguj stany:

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
Dzięki za informacje dotyczące „przeciwnych” stanów!
BVB

Można tego użyć do zmiany koloru fab z biblioteki projektów.
Tapirboy,

5
OSTRZEŻENIE: Zapoznaj się z odpowiedzią Rogera Aliena (i jej pierwszym komentarzem), aby zrozumieć, że kolejność stanów w tym miejscu jest słaba: ponieważ „włączony” jest pierwszy, zastąpi on inne stany, które zwykle występują, gdy przycisk jest włączony. Lepiej umieścić „włączony” na końcu. (Lub zamiast „włączony”, pusty / domyślny element na końcu.)
ToolmakerSteve

2
Podstawowa lista stanów dla przycisku, który nie zachowuje stan (nie przełącznik / checkbox) może być {pressed}, {focused}, {-enabled}, {}. Przez to może być przełącznik {checked, pressed}, {pressed}, {checked, focused}, {focused}, {checked}, {-enabled}, {}. Lub przełącznik, który ignoruje skupić: {checked, pressed}, {pressed}, {checked}, {-enabled}, {}.
ToolmakerSteve

Jeśli ktoś spróbuje któregoś z tych rozwiązań, zwróć uwagę na kolejność stanów jak w selector.xml!
Anton Makov

75

Pierwszy wymiar to tablica zestawów stanów, drugi to sam zestaw stanów. Tablica kolorów zawiera kolory dla każdego pasującego zestawu stanów, dlatego długość tablicy kolorów musi pasować do pierwszego wymiaru tablicy stanów (w przeciwnym razie nastąpi awaria, gdy stan jest „używany”). Tutaj i przykład:

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

mam nadzieję że to pomoże.

Przykład EDYCJI: lista stanów kolorów XML, na przykład:

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

wyglądałoby tak

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

Czy możesz powiedzieć, jak przedstawić poniższy kod 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> "używając colorstatelist.
Satish

@SatishKumar sprawdź moją edycję, chociaż jej nie testowałem.
Su-Au Hwang

3
Warto powiedzieć, że aby określić stan fałszywy, możesz zanegować jego wartość, więc jeśli chcesz określić kolor, gdy nie jest wciśnięty, powinieneś użyć: new int [] {- ​​android.R.attr.state_pressed}
tinsukE

1
Aby dodać do tego, co powiedział @tinsukE: Jednak aby uniknąć przypadkowego wyłączenia pozycji w dalszej części listy, dla większości stanów nie ma sensu umieszczanie negacji - zamiast tego obsłuż wszystkie „inne” możliwości z domyślnym (pustym) elementem new int[]{}ostatni - jak pokazano w ostatnim bloku kodu tej odpowiedzi. Jedyną zanegowaną wartością, której zwykle używam, jest „-enabled”. Inny przykład, jeśli chcesz, trzy różne kolory: „koncentruje + wciśnięty”, „koncentruje + nie wciśnięty”, „wciśnięty + nie koncentruje się”, można po prostu umieścić {focused, pressed}, {focused}, {pressed}. Zostanie użyty pierwszy „prawdziwy”.
ToolmakerSteve

2
... Pomyłka może zrobić to za sobą szereg podobnego {pressed}, {-pressed}, {focused}, {-focused}. Problem w tym, że {pressed}i {-pressed}obejmują WSZYSTKIE możliwości (przycisk jest wciśnięty lub nie), więc żadne kolory wymienione później nigdy nie będą używane.!
ToolmakerSteve

64

Czasami to wystarczy:

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

20

Niestety żadne z rozwiązań nie działa dla mnie.

  1. Jeśli nie ustawisz stanu wciśniętego na początku, nie wykryje go.
  2. Jeśli to ustawisz, musisz zdefiniować pusty stan, aby dodać domyślny kolor
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
        }
);

Oto konstruktor z kodu źródłowego:

/**
 * 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
Tak jak na marginesie: musisz traktować to tak, jak gdybyś to zrobił. Wybiera pierwszy stan, który jest prawdziwy. Tak więc, jeśli masz state_enabled jako pierwszy stan, zostanie on wybrany przed state_pressed - chyba że widok jest wyłączony.
LeoFarage

FWIW, skoro masz domyślny element jako ostatni, nie sądzę, żeby pierwszy „włączony” element w ogóle ci pomagał. Dlaczego nie usunąć go całkowicie?
ToolmakerSteve

18

Oto przykład, jak ColorListprogramowo stworzyć w Kotlinie:

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

Zobacz także moją odpowiedź poniżej dotyczącą funkcji pomocniczej Kotlin.
arekolek

7

Odbijając się od odpowiedzi Jonathana Ellisa , w Kotlinie możesz zdefiniować funkcję pomocniczą, aby kod był nieco bardziej idiomatyczny i łatwiejszy do odczytania, więc możesz zamiast tego napisać to:

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

colorStateListOf można zaimplementować w następujący sposób:

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

Też mam:

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

Aby móc wywołać tę samą nazwę funkcji, bez względu na to, czy jest to selektor, czy pojedynczy kolor.


3

Moja klasa konstruktora do tworzenia 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;
    }
}

Przykład użycia

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

jeśli korzystasz z zasobu 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);

2
jako getResources()przestarzałe, jest teraz ContextCompat.getColor(this,R.color.colorname);lub ContextCompat.getColor(getActivity(),R.color.colorname);do użycia we fragmencie
iBobb

Dla wyjaśnienia dla innych czytelników new int[0](jako element na liście pierwszego parametru) jest tablicą o zerowej długości i reprezentuje ustawienie domyślnego koloru. Tutaj jest to jedyny element, co oznacza, że ​​odcień jest stosowany do wszystkich stanów przycisku. Jest to równoważne z odpowiedzią new int[]{}Rogera Aliena.
ToolmakerSteve
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.