Materialny efekt na przycisku z kolorem tła

246

Korzystam z biblioteki obsługi Androida v21.

Utworzyłem przycisk z niestandardowym kolorem tła. Efekty projektowania materiałów, takie jak marszczenie, odsłanianie, znikają (oprócz elewacji po kliknięciu), gdy używam koloru tła.

 <Button
 style="?android:attr/buttonStyleSmall"
 android:background="?attr/colorPrimary"
 android:textColor="@color/white"
 android:textAllCaps="true"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Button1"
 />

tło Poniżej znajduje się normalny przycisk, a efekty działają dobrze.

<Button
 style="?android:attr/buttonStyleSmall"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:textAllCaps="true"
 android:text="Button1"
/>

Domyślny przycisk

Sathesh
źródło
9
Dzieje się tak, ponieważ zastępujesz tło wszystkimi ładnymi efektami materiałowymi jednolitym kolorem. Jeśli chodzi o to, jak prawidłowo pokolorować przycisk, zachowując efekty materialne, nikt na SO nie był jeszcze w stanie tego zrozumieć.
Nathan Walters
2
@SweetWisher Tętnienie i inne efekty nie są stosowane w przycisku, gdy zmieniam kolor tła.
Sathesh
1
@NathanWalters Ale domyślny szary kolor jest brzydki. :-(
Sathesh
6
Uwierz mi, wiem ... Będziemy bardzo wdzięczni temu, kto wymyśli, jak to zrobić poprawnie.
Nathan Walters
1
Niesamowita odpowiedź stackoverflow.com/a/32238489/1318946
Pratik Butani

Odpowiedzi:

431

Podczas używania android:backgroundzamieniasz większość stylizacji i wyglądu przycisku na pusty kolor.

Aktualizacja: W tej wersji 23.0.0 zwolnienia z AppCompat , pojawił się nowy Widget.AppCompat.Button.Coloredstyl, który wykorzystuje swoją motywu colorButtonNormaldla niepełnosprawnych koloru i colorAccentdla włączonego koloru.

Pozwala to zastosować go bezpośrednio do przycisku

<Button
  ...
  style="@style/Widget.AppCompat.Button.Colored" />

Jeśli potrzebujesz niestandardowego colorButtonNormallub colorAccent, możesz użyć narzędzia ThemeOverlayopisanego w tym poradniku i android:themena przycisku.

Poprzednia odpowiedź

Możesz użyć rysowania w katalogu v21 dla swojego tła, takiego jak:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="?attr/colorControlHighlight">
    <item android:drawable="?attr/colorPrimary"/>
</ripple>

Zapewni to ?attr/colorPrimarydomyślny kolor tła i domyślną animację tętnienia przy użyciu domyślnej ?attr/colorControlHighlight(którą możesz również ustawić w motywie, jeśli chcesz).

Uwaga: musisz utworzyć niestandardowy selektor dla wersji mniejszej niż 21:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/primaryPressed" android:state_pressed="true"/>
    <item android:drawable="@color/primaryFocused" android:state_focused="true"/>
    <item android:drawable="@color/primary"/>
</selector>

Zakładając, że masz kilka kolorów, które chcesz ustawić w stanie domyślnym, wciśniętym i skupionym. Osobiście zrobiłem zrzut ekranu tętnienia w połowie drogi i zostałem z tego wyciągnięty stan podstawowy / skupiony.

ianhanniballake
źródło
13
Niezła odpowiedź! Dokładnie to, czego szukałem, ale musiałem wyczyścić i przebudować, zanim projekt zdecydował się zidentyfikować drawable w drawable-v21.
user1354603
4
@ianhanniballake Zastanawiam się, jak połączyć tę działającą technikę, nie tracąc przy tym zaokrąglania narożników w tych przyciskach
Joaquin Iurchuk
3
@ianhanniballake to tylko komentarz, który można narysować w sposób ciągły pod <item android: drawable ... /> nie może być przezroczysty. W takim przypadku tętnienie nie zostanie wyświetlone. Zajęło mi to trochę czasu, aby to zrozumieć.
Ivan Kušt
2
Z appcompat 23.1.1 i API 22, zarówno stan wyłączony, jak i włączony, przyjmują kolor szablonu ColorButtonNormal.
Rémy DAVID
93

Istnieje inne proste rozwiązanie polegające na zapewnieniu niestandardowego tła dla przycisków „płaskich”, przy jednoczesnym zachowaniu ich efektów „materiałowych”.

  1. Umieść przycisk w ViewGroup z ustawionym tam pożądanym tłem
  2. ustaw selectableItemBackground z bieżącego motywu jako tło dla twojego przycisku (API> = 11)

tj .:

<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/blue">
    <Button
        style="?android:attr/buttonStyleSmall"
        android:background="?android:attr/selectableItemBackground"
        android:textColor="@android:color/white"
        android:textAllCaps="true"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Button1"
        />
</FrameLayout>

Może być użyty do płaskich przycisków, działa na API> = 11, a otrzymasz efekt tętnienia na urządzeniach> = 21, utrzymując zwykłe przyciski na wersjach wcześniejszych niż 21, dopóki AppCompat nie zostanie zaktualizowany, aby obsługiwał również tam tętnienie.

Możesz także użyć selectableItemBackgroundBorderless tylko dla przycisków> = 21.

kiruwka
źródło
Dosłownie pomyślałeś po wyjęciu z pudełka :-) Chociaż jest to jeden ze sposobów, może to zwiększyć sprzężenie kodu i użycie stylów byłoby zalecanym sposobem.
Sathesh
3
To tworzy płaski przycisk z kolorowym tłem, tak. Ale utworzony przycisk wygląda jak kwadrat, bez zaokrąglonych rogów i efektów cienia.
Sanket Berde
7
@SanketBerde Dokładnie. Bo to właśnie jest „płaski” przycisk.
kiruwka
2
W pierwotnym pytaniu nie wspomniano o mieszkaniu. btw, jeśli masz w swoim rozwiązaniu element nadrzędny <CardView> zamiast <FrameLayout>, powstały przycisk zostanie podniesiony i będzie miał cienie. Działa to nawet w wersjach pre-Lollipop.
Sanket Berde
1
@SanketBerde wspaniale wiedzieć o CardViewpodejściu, dziękuję za udostępnienie!
kiruwka
88

Oto prosty i kompatybilny wstecz sposób, aby zapewnić efekt falowania podniesionym przyciskom z niestandardowym tłem.

Twój układ powinien wyglądać tak

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/my_custom_background"
    android:foreground="?android:attr/selectableItemBackground"/>
Bolein95
źródło
9
Jest to najłatwiejszy bezproblemowy sposób na osiągnięcie tego. Dobra odpowiedź.
Tyler Pfaff,
1
api 21+, ale nie działa. Stosowany w układzie liniowym z klikalnym-prawdziwym, tło - inny kolor, określony na pierwszym planie.
Alex
3
Ponieważ Button jest TextView, a nie FrameLayout, foregroundnie działa przed 23. google.com/…
androidguy
2
MIŁY! Nawet nie wiedziałem, że „pierwszy plan” to coś!
Eric Schlenz
2
Działa jak marzenie..!
Kannan
25

Natknąłem się na ten problem dzisiaj, grając z biblioteką v22.

Zakładając, że używasz stylów, możesz ustawić colorButtonNormalwłaściwość, a przyciski będą domyślnie używać tego koloru.

<style name="AppTheme" parent="BaseTheme">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/primaryColor</item>
    <item name="colorPrimaryDark">@color/primaryColorDark</item>
    <item name="colorAccent">@color/accentColor</item>
    <item name="colorButtonNormal">@color/primaryColor</item>
</style>

Poza tym nadal możesz stworzyć styl dla przycisku, a następnie użyć go dla każdego przycisku, jeśli potrzebujesz asortymentu kolorów (nie testowałem, tylko spekulujesz).

Pamiętaj, aby dodać android:przed nazwami elementów w swoim stylu v21 .

Robert Rose
źródło
Aby zobaczyć asortyment kolorowych przycisków, zobacz inną odpowiedź Roberta: stackoverflow.com/a/31825439/1617737
zakaz geoinżynierii
Czy po ustawieniu koloru tła istnieje także ogólny atrybut, którego można użyć w powyższym bloku kodu, aby określić kolor tekstu przycisku?
zakaz geoinżynierii
24

Za pomocą AppCompat (22.1.1+) możesz dodać taki styl:

<style name="MyGreenButton">
    <item name="colorButtonNormal">#009900</item>
</style>

I użyj go, po prostu stosując styl:

<android.support.v7.widget.AppCompatButton
    style="@style/MyGreenButton"
    android:layout_width="match_width"
    android:layout_height="wrap_content"
    android:text="A Green Button"
    />

Programowo zmieniając kolor, odkryłem, że jedynym sposobem na aktualizację koloru (w API 15 lub 16) było użycie zamiast tego „listy odcieni tła”. I nie usuwa ładnej animacji radialnej na urządzeniach API 21:

ColorStateList colorStateList = new ColorStateList(new int[][] {{0}}, new int[] {0xFF009900}); // 0xAARRGGBB
button.setSupportBackgroundTintList(colorStateList);

Ponieważ button.setBackground(...)i button.getBackground().mutate().setColorFilter(...)nie zmieniaj koloru przycisku w API 15 i 16, tak jak w API 21.

Robert
źródło
1
Dziękuję bardzo, działało to dla mnie idealnie (nawet jeśli użycie przycisku w układach xml, ponieważ inflator AppCompat zamienia go na AppCompatButton)
Louis CAD
1
Użyj, ColorStateList.valueOf( ... )aby skonstruować prosty ColorStateListtylko jeden kolor.
Taig
colorAccent i rippleColor nie pracują w stylu
Yar
Dziękuję za ten post. Co zadziałało dla mnie: ViewCompat.setBackgroundTintList(myButton, ColorStateList.valueOf(myColor); Javadoc dla setSupportBackgroundTintListmówi, że należy to tak nazwać.
JerabekJakub
22

Odpowiedź @ ianhanniballake jest absolutnie poprawna i prosta. Ale zrozumienie zajęło mi kilka dni. Dla kogoś, kto nie rozumie jego odpowiedzi, oto bardziej szczegółowe wdrożenie

<Button
        android:id="@+id/btn"
        style="@style/MaterialButton"
        ... />


<style name="MaterialButton" parent="Widget.AppCompat.Button.Colored">
    <item name="android:theme">@style/Theme.MaterialButton</item>
   ...
</style>


<style name="Theme.MaterialButton" parent="YourTheme">
    <item name="colorAccent">@color/yourAccentColor</item>
    <item name="colorButtonNormal">@color/yourButtonNormalColor</item>
</style>

=== Lub ===

<Button
        android:id="@+id/btn"
        style="@style/Widget.AppCompat.Button.Colored"
        android:theme="@style/Theme.MaterialButton" />

<style name="Theme.MaterialButton" parent="YourTheme">
    <item name="colorAccent">@color/yourAccentColor</item>
    <item name="colorButtonNormal">@color/yourButtonNormalColor</item>
</style>
Frank Nguyen
źródło
5
Twoja odpowiedź była dla mnie ostatnim elementem układanki. Można również pisać colorButtonNormalw temacie podstawowym @style/AppTheme. To nadaje domyślny kolor AppCompat, aby odpowiednio pokolorować przyciski. A potem możesz użyć swojej odpowiedzi, aby indywidualnie stworzyć motyw widoku.
Siddharth Pant
1
Czy to android:theme="Theme.MaterialButton"lubandroid:theme="@style/Theme.MaterialButton"
osrl
1
@osrl It's android:theme="@style/Theme.MaterialButton". Dziękuję za twoją poprawność.
Frank Nguyen,
12

Użyłem tła i pierwszego planu:

<Button
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:backgroundTint="@color/colorAccent"
    android:foreground="?android:attr/selectableItemBackground"
    android:textColor="@android:color/white"
    android:textSize="10sp"/>
SpyZip
źródło
afaik, nie ma potrzeby używania atrybutu pierwszego planu, ponieważ backgroundTint nie zastępuje standardowego tętnienia
Julian Os
7

Przyszedłem do tego postu, szukając sposobu, aby uzyskać kolor tła mojego ListViewprzedmiotu, a jednocześnie zachować falę.

Po prostu dodałem swój kolor jako tło i selectableItemBackground jako pierwszy plan:

<style name="my_list_item">
    <item name="android:background">@color/white</item>
    <item name="android:foreground">?attr/selectableItemBackground</item>
    <item name="android:layout_height">@dimen/my_list_item_height</item>
</style>

i działa jak urok. Wydaje mi się, że ta sama technika może być zastosowana również do przycisków. Powodzenia :)

Joakim
źródło
6

Jeśli interesuje Cię ImageButton, możesz wypróbować tę prostą rzecz:

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@android:drawable/ic_button"
    android:background="?attr/selectableItemBackgroundBorderless"
/>
Ameer Khalifullah
źródło
5

v22.1od appcompat-v7wprowadzono kilka nowych możliwości. Teraz możliwe jest przypisanie określonego motywu tylko do jednego widoku.

Przestarzałe korzystanie z aplikacji: motyw do stylizacji paska narzędzi. Teraz możesz używać systemu Android: motyw do pasków narzędzi na wszystkich urządzeniach interfejsu API poziomu 7 i wyższych oraz systemu Android: obsługa motywów dla wszystkich widgetów na poziomie interfejsu API 11 i wyższych urządzeń.

Zamiast ustawiać pożądany kolor w motywie globalnym, tworzymy nowy i przypisujemy go tylko do Button

Przykład:

<style name="MyColorButton" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorButtonNormal">@color/myColor</item>
</style>

I użyj tego stylu jako motywu swojego Button

<Button
 style="?android:attr/buttonStyleSmall"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Button1"
 android:theme="@style/MyColorButton"/>
Bhargav Thanki
źródło
4

Jeśli nie masz nic przeciwko korzystaniu z biblioteki innej firmy, sprawdź traex / RippleEffect . Pozwala dodać efekt Ripple do KAŻDEGO widoku za pomocą zaledwie kilku linii kodu. Trzeba tylko owinąć w pliku układu xml element, który ma wywoływać efekt marszczenia z com.andexert.library.RippleViewkontenerem.

Jako dodatkowy bonus wymaga Min SDK 9, abyś mógł zachować spójność projektu we wszystkich wersjach systemu operacyjnego.

Oto przykład zaczerpnięty z repozytorium GitHub bibliotek:

<com.andexert.library.RippleView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:rv_centered="true">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@android:drawable/ic_menu_edit"
        android:background="@android:color/holo_blue_dark"/> 

</com.andexert.library.RippleView>

Możesz zmienić kolor tętnienia, dodając ten atrybut element RippleView: app:rv_color="@color/my_fancy_ripple_color

guy.gc
źródło
3
<android.support.v7.widget.AppCompatButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:backgroundTint="#fff"
    android:textColor="#000"
    android:text="test"/>

Posługiwać się

xmlns:app="http://schemas.android.com/apk/res-auto"

a kolor zaakceptuje pre-lolipop

André Bäuml
źródło
2

Istnieją dwa podejścia wyjaśnione we wspaniałym samouczku autorstwa Alexa Lockwooda: http://www.androiddesignpatterns.com/2016/08/coloring-buttons-with-themeoverlays-background-tints.html :

Podejście nr 1: Modyfikacja koloru tła przycisku w / ThemeOverlay

<!-- res/values/themes.xml -->
<style name="RedButtonLightTheme" parent="ThemeOverlay.AppCompat.Light">
    <item name="colorAccent">@color/googred500</item>
</style>

<Button
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:theme="@style/RedButtonLightTheme"/>

Podejście nr 2: Ustawianie odcienia tła AppCompatButton

<!-- res/color/btn_colored_background_tint.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Disabled state. -->
    <item android:state_enabled="false"
          android:color="?attr/colorButtonNormal"
          android:alpha="?android:attr/disabledAlpha"/>

    <!-- Enabled state. -->
    <item android:color="?attr/colorAccent"/>

</selector>

<android.support.v7.widget.AppCompatButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:backgroundTint="@color/btn_colored_background_tint"/>
makovkastar
źródło
2

Programowe stosowanie kolorów:

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {

    ColorStateList colorStateListRipple = new ColorStateList(
            new int[][] {{0}},
            new int[] {Color.WHITE} // ripple color
            );

    RippleDrawable rippleDrawable = (RippleDrawable) myButton.getBackground();
    rippleDrawable.setColor(colorStateListRipple);
    myButton.setBackground(rippleDrawable); // applying the ripple color
}

ColorStateList colorStateList = new ColorStateList(
        new int[][]{
                new int[]{android.R.attr.state_pressed}, // when pressed
                new int[]{android.R.attr.state_enabled}, // normal state color
                new int[]{} // normal state color
        },
        new int[]{
                Color.CYAN, // when pressed
                Color.RED, // normal state color
                Color.RED // normal state color
        }
);

ViewCompat.setBackgroundTintList(myButton, colorStateList); // applying the state colors
Marlon
źródło
-1

Próbowałem tego:

android:backgroundTint:"@color/mycolor"

zamiast zmiany właściwości tła. Nie usuwa to efektu materialnego.

shiladitya
źródło
Nie będzie to działać w przypadku urządzeń sprzed wersji Lollipop (70% udziału w rynku). W rzeczywistości został przeniesiony, ale Android Studio nadal pokazuje błędy podczas korzystania z niego bez prefiksu Androida.
Siddharth Pant