Effetto materiale sul pulsante con il colore di sfondo


246

Sto usando la libreria di supporto di Android v21.

Ho creato un pulsante con il colore di sfondo personalizzato. Gli effetti di disegno Materiale come ondulazione, rivelazione scompaiono (tranne l'elevazione al clic) quando uso il colore di sfondo posteriore.

 <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"
 />

sfondo Quello che segue è un pulsante normale e gli effetti funzionano bene.

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

Pulsante predefinito


9
Ciò si verifica perché si sostituisce lo sfondo con tutti i bei effetti Materiale con un colore uniforme. Quanto a come si colora correttamente un pulsante mantenendo gli effetti Materiale, nessuno su SO è stato ancora in grado di capirlo.
Nathan Walters,

2
@SweetWisher L'increspatura e gli altri effetti non vengono applicati al pulsante quando cambio il colore di sfondo.
Sathesh,

1
@NathanWalters Ma il colore grigio predefinito è brutto. :-(
Sathesh,

6
Credimi, lo so ... Saremo in debito con chiunque capirà come farlo correttamente.
Nathan Walters,

Risposte:


431

Quando si utilizza android:background, si sostituisce gran parte dello stile e l'aspetto di un pulsante con un colore bianco.

Aggiornamento: a partire dalla versione 23.0.0 di AppCompat , esiste un nuovo Widget.AppCompat.Button.Coloredstile che utilizza i temi colorButtonNormalper il colore disabilitato e colorAccentper il colore abilitato.

Ciò ti consente di applicarlo direttamente al tuo pulsante tramite

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

Se hai bisogno di una personalizzazione colorButtonNormalo colorAccent, puoi usare una ThemeOverlaycome spiegato in questo suggerimento e android:themesul pulsante.

Risposta precedente

È possibile utilizzare un disegno nella directory v21 per lo sfondo come:

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

Ciò assicurerà che il colore di sfondo sia ?attr/colorPrimarye abbia l'animazione di ondulazione predefinita usando quella predefinita ?attr/colorControlHighlight(che puoi anche impostare nel tuo tema se lo desideri).

Nota: dovrai creare un selettore personalizzato per meno di v21:

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

Supponendo che tu abbia alcuni colori che desideri per lo stato predefinito, premuto e focalizzato. Personalmente, ho fatto uno screenshot di un'increspatura a metà della selezione e ne ho tirato fuori lo stato primario / focalizzato.


13
Bella risposta! Esattamente quello che stavo cercando, tuttavia era necessario pulirlo e ricostruirlo prima che il progetto decidesse di identificare il disegno in drawable-v21.
user1354603


4
@ianhanniballake Mi chiedo come combinare questa tecnica di lavoro senza perdere la personalizzazione degli angoli arrotondati in quei pulsanti
Joaquin Iurchuk

3
@ianhanniballake solo un commento, disegnabile in ripple sotto <item android: drawable ... /> non può essere un colore trasparente. In tal caso, l'ondulazione non verrà mostrata. Mi ci è voluto un po 'per rendermene conto.
Ivan Kušt,

2
Con appcompat 23.1.1 e API 22, entrambi gli stati disabilitato e abilitato assumono il colore colorButtonNormal del tema.
Rémy DAVID,

93

Esiste un'altra soluzione semplice per fornire uno sfondo personalizzato per i pulsanti "piatti" mantenendo gli effetti "Materiale".

  1. Posiziona il pulsante in ViewGroup con lo sfondo desiderato impostato lì
  2. imposta selectableItemBackground dal tema corrente come sfondo per il tuo pulsante (API> = 11)

cioè:

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

Può essere usato per i pulsanti Flat , funziona su API> = 11 e otterrai un effetto a catena su> = 21 dispositivi, mantenendo i pulsanti regolari su pre-21 fino a quando AppCompat non viene aggiornato per supportare anche l'ondulazione lì.

Puoi anche usare selectableItemBackgroundBorderless solo per i pulsanti> = 21.


Hai letteralmente pensato fuori dagli schemi :-) Anche se questo è un modo per farlo, potrebbe aumentare l'accoppiamento del codice e l'uso degli stili sarebbe il modo raccomandato.
Sathesh,

3
Questo crea un pulsante piatto con sfondo colorato, sì. Ma il pulsante creato sembra un quadrato, senza angoli arrotondati ed effetti d'ombra.
Sanket Berde,

7
@SanketBerde Esattamente. Perché questo è il pulsante "piatto".
Kiruwka,

2
La domanda originale non menzionava l'appartamento. tra l'altro, se nella soluzione è presente un genitore <CardView> anziché <Lame frame>, il pulsante risultante verrà sollevato e avrà ombre. Funziona anche nelle versioni pre-lecca-lecca.
Sanket Berde,

1
@SanketBerde ottimo per conoscere l' CardViewapproccio, grazie per la condivisione!
Kiruwka,

88

Ecco un modo semplice e retrocompatibile per fornire l'effetto a catena ai pulsanti in rilievo con lo sfondo personalizzato.

Il layout dovrebbe apparire così

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/my_custom_background"
    android:foreground="?android:attr/selectableItemBackground"/>

9
Questo è il modo più semplice per farlo. Buona risposta.
Tyler Pfaff,

1
api 21+ ma non funziona. Applicato su layout lineare con sfondo selezionabile, vero - colore diverso, primo piano specificato.
Alex,

3
Poiché Button è un TextView e non un FrameLayout, foregroundnon ha alcun effetto prima del 23. google.com/…
androidguy il

2
SIMPATICO! Non sapevo nemmeno che "in primo piano" fosse una cosa!
Eric Schlenz,

2
Funziona come un fascino ..!
Kannan,

25

Ho riscontrato questo problema oggi giocando con la libreria v22.

Supponendo che tu stia utilizzando gli stili, puoi impostare la colorButtonNormalproprietà e i pulsanti utilizzeranno quel colore per impostazione predefinita.

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

Al di fuori di ciò, puoi comunque creare uno stile per il pulsante, quindi utilizzarlo per ogni pulsante se hai bisogno di un assortimento di colori (non ho testato, solo speculato).

Ricorda di aggiungere android:prima dei nomi degli oggetti nel tuo stile v21 .


Per un assortimento di pulsanti colorati, vedi la risposta di altri Robert: stackoverflow.com/a/31825439/1617737
ban-geoengineering

Con il colore di sfondo ora impostato, esiste anche un attributo generale che posso usare nel blocco di codice sopra per specificare il colore del testo del pulsante?
divieto di geoingegneria il

24

Con AppCompat (22.1.1+) puoi aggiungere uno stile come questo:

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

E usalo semplicemente applicando lo stile:

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

Modificando a livello di codice il colore, ho scoperto che l'unico modo per aggiornare il colore (su API 15 o 16) era invece usare la "lista tinta di sfondo". E non rimuove la bella animazione radiale sui dispositivi API 21:

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

Perché button.setBackground(...)e button.getBackground().mutate().setColorFilter(...)non cambiare il colore dei pulsanti su API 15 e 16 come fanno su API 21.


1
Grazie mille, ha funzionato perfettamente per me (anche se si utilizza Button nei layout xml come il gonfiatore AppCompat lo sostituisce con AppCompatButton)
Louis CAD

1
Utilizzare ColorStateList.valueOf( ... )per costruire un semplice ColorStateListdi un solo colore.
Taig,

colorAccent e rippleColor non funzionano nello stile
Yar

Grazie per questo post. Cosa ha funzionato per me: ViewCompat.setBackgroundTintList(myButton, ColorStateList.valueOf(myColor); Javadoc per setSupportBackgroundTintListdice che dovrebbe essere chiamato in questo modo.
JerabekJakub,

22

La risposta di @ ianhanniballake è assolutamente corretta e semplice. Ma mi ci sono voluti alcuni giorni per capire. Per qualcuno che non capisce la sua risposta, ecco un'implementazione più dettagliata

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

=== === O

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

5
La tua risposta è stata l'ultimo pezzo del puzzle per me. Si può anche scrivere colorButtonNormalnel tema di base @style/AppTheme. Ciò fornisce un colore predefinito ad AppCompat per colorare i pulsanti di conseguenza. E quindi puoi utilizzare la tua risposta per personalizzare una vista a tema.
Siddharth Pant,

1
È android:theme="Theme.MaterialButton"oandroid:theme="@style/Theme.MaterialButton"
osrl il

1
@osrl Lo è android:theme="@style/Theme.MaterialButton". Grazie per la tua correttezza.
Frank Nguyen l'

12

Ho usato backgroundTint e primo piano:

<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"/>

afaik non è necessario per l'attributo in primo piano poiché backgroundTint non ignora l'ondulazione standard
Julian Os

7

Sono venuto a questo post in cerca di un modo per avere un colore di sfondo per il mio ListViewoggetto, pur mantenendo l'increspatura.

Ho semplicemente aggiunto il mio colore come sfondo e l'ItemBackground selezionabile come primo piano:

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

e funziona come un fascino. Immagino che la stessa tecnica possa essere usata anche per i pulsanti. In bocca al lupo :)



6

Se sei interessato a ImageButton puoi provare questa semplice cosa:

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@android:drawable/ic_button"
    android:background="?attr/selectableItemBackgroundBorderless"
/>

5

v22.1di appcompat-v7introdotto alcune nuove possibilità. Ora è possibile assegnare un tema specifico solo a una vista.

Uso obsoleto dell'app: tema per lo styling della barra degli strumenti. Ora puoi usare android: tema per le barre degli strumenti su tutti i dispositivi API livello 7 e successivi e android: supporto per temi per tutti i widget su API livello 11 e superiori.

Quindi, invece di impostare il colore desiderato in un tema globale, ne creiamo uno nuovo e lo assegniamo solo a Button

Esempio:

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

E usa questo stile come tema per il tuo Button

<Button
 style="?android:attr/buttonStyleSmall"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Button1"
 android:theme="@style/MyColorButton"/>

4

Se stai bene usando una libreria di terze parti, dai un'occhiata a traex / RippleEffect . Ti consente di aggiungere un effetto a catena a QUALSIASI vista con solo poche righe di codice. Devi solo avvolgere, nel tuo file di layout XML, l'elemento che vuoi avere un effetto a catena con un com.andexert.library.RippleViewcontenitore.

Come bonus aggiuntivo richiede SDK Min 9 in modo da poter avere coerenza progettuale tra le versioni del sistema operativo.

Ecco un esempio tratto dal repository GitHub delle librerie:

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

È possibile modificare il colore dell'ondulazione aggiungendo questo attributo all'elemento RippleView: app:rv_color="@color/my_fancy_ripple_color


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"/>

Uso

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

e il colore accetterà su pre-lolipop


2

Ci sono due approcci spiegati nel fantastico tutorial di Alex Lockwood: http://www.androiddesignpatterns.com/2016/08/coloring-buttons-with-themeoverlays-background-tints.html :

Approccio n. 1: modifica del colore di sfondo del pulsante con un tema di sovrapposizione

<!-- 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"/>

Approccio n. 2: impostazione della tonalità di sfondo di 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"/>

2

Applicazione a livello di codice dei colori:

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

-1

Ho provato questo:

android:backgroundTint:"@color/mycolor"

invece di modificare la proprietà di sfondo. Questo non rimuove l'effetto materiale.


Questo non funzionerà con i dispositivi pre-Lollipop (quota di mercato del 70%). In realtà è stato eseguito il backport ma Android Studio al momento mostra ancora errori quando lo si utilizza senza il prefisso Android.
Siddharth Pant,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.