Effet matériel sur bouton avec couleur de fond

246

J'utilise la bibliothèque de support Android v21.

J'ai créé un bouton avec une couleur d'arrière-plan personnalisée. Les effets de conception de matériau comme l'ondulation, la révélation ont disparu (sauf l'élévation au clic) lorsque j'utilise la couleur de fond.

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

Contexte Ce qui suit est un bouton normal et les effets fonctionnent très bien.

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

Bouton par défaut

Sathesh
la source
9
Cela se produit car vous remplacez l'arrière-plan par tous les beaux effets de matériau par une couleur unie. En ce qui concerne la façon de colorer correctement un bouton tout en conservant les effets de matériau, personne sur SO n'a pu encore le comprendre.
Nathan Walters
2
@SweetWisher L'ondulation et les autres effets ne sont pas appliqués dans le bouton lorsque je change la couleur d'arrière-plan.
Sathesh
1
@NathanWalters Mais la couleur grise par défaut est moche. :-(
Sathesh
6
Croyez-moi, je sais ... Nous serons très redevables à quiconque trouvera comment le faire correctement.
Nathan Walters du
1
Awesome answer stackoverflow.com/a/32238489/1318946
Pratik Butani

Réponses:

431

Lorsque vous utilisez android:background, vous remplacez une grande partie du style et de l'apparence d'un bouton par une couleur vierge.

Mise à jour: Depuis la version 23.0.0 d' AppCompat , il existe un nouveau Widget.AppCompat.Button.Coloredstyle qui utilise votre thème colorButtonNormalpour la couleur désactivée et colorAccentpour la couleur activée.

Cela vous permet de l'appliquer directement sur votre bouton via

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

Si vous avez besoin d'un colorButtonNormalou personnalisé colorAccent, vous pouvez utiliser un ThemeOverlaycomme expliqué dans ce pro-tip et android:themesur le bouton.

Réponse précédente

Vous pouvez utiliser un dessin dans votre répertoire v21 pour votre arrière-plan, comme:

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

Cela garantira que votre couleur d'arrière-plan est ?attr/colorPrimaryet a l'animation d'ondulation par défaut en utilisant la valeur par défaut ?attr/colorControlHighlight(que vous pouvez également définir dans votre thème si vous le souhaitez).

Remarque: vous devrez créer un sélecteur personnalisé pour moins de 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>

En supposant que vous ayez certaines couleurs que vous souhaitez pour l'état par défaut, pressé et concentré. Personnellement, j'ai pris une capture d'écran d'une ondulation au milieu de la sélection et j'ai retiré l'état principal / concentré de cela.

ianhanniballake
la source
13
Bonne réponse! Exactement ce que je cherchais, mais j'avais besoin de nettoyer et de reconstruire avant que le projet ne décide d'identifier le drawable dans drawable-v21.
user1354603
4
@ianhanniballake Je me demande comment combiner cette technique de travail sans perdre la personnalisation des coins arrondis dans ces boutons
Joaquin Iurchuk
3
@ianhanniballake juste un commentaire, dessinable en ondulation sous <item android: drawable ... /> ne peut pas être une couleur transparente. Dans ce cas, l'ondulation ne sera pas affichée. Il m'a fallu un certain temps pour réaliser cela.
Ivan Kušt
2
Avec appcompat 23.1.1 et API 22, l'état désactivé et activé prennent tous deux la couleur colorButtonNormal du thème.
Rémy DAVID
93

Il existe une autre solution simple pour fournir un arrière-plan personnalisé aux boutons «plats» tout en conservant leurs effets «matériel».

  1. Placez votre bouton dans ViewGroup avec l'arrière-plan souhaité défini
  2. définissez selectableItemBackground du thème actuel comme arrière-plan pour votre bouton (API> = 11)

c'est à dire :

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

Peut être utilisé pour les boutons plats , il fonctionne sur l'API> = 11, et vous obtiendrez un effet d'entraînement sur les appareils> = 21, en gardant les boutons réguliers sur la pré-21 jusqu'à ce que AppCompat soit mis à jour pour prendre en charge l'ondulation également.

Vous pouvez également utiliser selectableItemBackgroundBorderless pour les boutons> = 21 uniquement.

kiruwka
la source
Vous avez littéralement pensé hors de la boîte :-) Bien que ce soit une façon de le faire, cela pourrait augmenter le couplage de code et l'utilisation de styles serait la manière recommandée.
Sathesh
3
Cela crée un bouton plat avec un fond de couleur, oui. Mais le bouton créé ressemble à un carré, sans coins arrondis et sans effets d'ombre.
Sanket Berde
7
@SanketBerde Exactement. Parce que c'est ça le bouton "plat".
kiruwka
2
La question d'origine ne mentionnait pas l'appartement. btw, si vous avez un parent <CardView> dans votre solution au lieu de <FrameLayout>, le bouton résultant sera levé et aura des ombres. Cela fonctionne même dans les versions pré-sucettes.
Sanket Berde
1
@SanketBerde super à savoir sur l' CardViewapproche, merci pour le partage!
kiruwka
88

Voici un moyen simple et rétrocompatible de fournir un effet d'entraînement aux boutons surélevés avec l'arrière-plan personnalisé.

Votre mise en page devrait ressembler à ceci

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/my_custom_background"
    android:foreground="?android:attr/selectableItemBackground"/>
Bolein95
la source
9
C'est le moyen le plus simple et sans tracas pour y parvenir. Bonne réponse.
Tyler Pfaff
1
api 21+ mais ne fonctionne pas. Appliqué sur une disposition linéaire avec un clic vrai, un arrière-plan - une couleur différente, un avant-plan spécifié.
Alex
3
Étant donné que Button est un TextView et non un FrameLayout, foregroundn'a aucun effet avant le 23. google.com/…
androidguy
2
AGRÉABLE! Je ne savais même pas que "le premier plan" était une chose!
Eric Schlenz
2
Fonctionne comme un charme ..!
Kannan
25

J'ai rencontré ce problème aujourd'hui en jouant avec la bibliothèque v22.

En supposant que vous utilisez des styles, vous pouvez définir la colorButtonNormalpropriété et les boutons utiliseront cette couleur par défaut.

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

En dehors de cela, vous pouvez toujours créer un style pour le bouton, puis l'utiliser par bouton si vous avez besoin d'un assortiment de couleurs (non testé, juste spéculant).

N'oubliez pas d'ajouter android:avant les noms des éléments dans votre style v21 .

Robert Rose
la source
Pour un assortiment de boutons colorés, voir la réponse de Robert: stackoverflow.com/a/31825439/1617737
ban-
Avec la couleur d'arrière-plan maintenant définie, y a-t-il également un attribut général que je peux utiliser dans le bloc de code ci-dessus pour spécifier la couleur du texte du bouton?
ban-geoengineering
24

Avec AppCompat (22.1.1+), vous pouvez ajouter un style comme celui-ci:

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

Et utilisez-le en appliquant simplement le style:

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

En modifiant la couleur par programme, j'ai trouvé que la seule façon de mettre à jour la couleur (sur l'API 15 ou 16) était d'utiliser à la place la «liste de teintes d'arrière-plan». Et cela ne supprime pas la belle animation radiale sur les appareils API 21:

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

Parce que button.setBackground(...)et button.getBackground().mutate().setColorFilter(...)ne changez pas la couleur des boutons sur les API 15 et 16 comme ils le font sur l'API 21.

Robert
la source
1
Merci beaucoup, cela a parfaitement fonctionné pour moi (même si vous utilisez Button dans les mises en page XML car le gonfleur AppCompat le remplace par AppCompatButton)
Louis CAD
1
Utilisez ColorStateList.valueOf( ... )pour construire un simple ColorStateListd'une seule couleur.
Taig
colorAccent et rippleColor ne fonctionnent pas dans le style
Yar
Merci pour ce post. Ce qui a fonctionné pour moi: ViewCompat.setBackgroundTintList(myButton, ColorStateList.valueOf(myColor); Javadoc pour setSupportBackgroundTintListdit que cela devrait être appelé ainsi.
JerabekJakub
22

La réponse de @ ianhanniballake est absolument correcte et simple. Mais il m'a fallu quelques jours pour comprendre. Pour quelqu'un qui ne comprend pas sa réponse, voici une implémentation plus détaillée

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

=== Ou ===

<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
la source
5
Votre réponse était la dernière pièce du puzzle pour moi. On peut également écrire colorButtonNormaldans le thème de base @style/AppTheme. Cela donne une couleur par défaut à AppCompat pour coloriser les boutons en conséquence. Et puis pouvez utiliser votre réponse pour thème une vue individuellement.
Siddharth Pant
1
Est-ce android:theme="Theme.MaterialButton"ouandroid:theme="@style/Theme.MaterialButton"
osrl
1
@osrl C'est android:theme="@style/Theme.MaterialButton". Merci pour votre justesse.
Frank Nguyen
12

J'ai utilisé le backgroundTint et le premier plan:

<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
la source
afaik il n'y a aucune nécessité pour l'attribut de premier plan car backgroundTint ne remplace pas l'ondulation standard
Julian Os
7

Je suis venu à ce poste à la recherche d'un moyen d'avoir une couleur d'arrière-plan de mon ListViewarticle, tout en conservant l'ondulation.

J'ai simplement ajouté ma couleur comme arrière-plan et le selectableItemBackground comme premier 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>

et cela fonctionne comme un charme. Je suppose que la même technique pourrait également être utilisée pour les boutons. Bonne chance :)

Joakim
la source
6

Si ImageButton vous intéresse, vous pouvez essayer cette chose simple:

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@android:drawable/ic_button"
    android:background="?attr/selectableItemBackgroundBorderless"
/>
Ameer Khalifullah
la source
5

v22.1de appcompat-v7introduit de nouvelles possibilités. Il est désormais possible d'assigner un thème spécifique à une seule vue.

Utilisation obsolète de l'application: thème pour le style de la barre d'outils. Vous pouvez désormais utiliser android: theme pour les barres d'outils sur tous les appareils API de niveau 7 et supérieur et android: theme pour tous les widgets sur API de niveau 11 et supérieur.

Ainsi, au lieu de définir la couleur souhaitée dans un thème global, nous en créons une nouvelle et l’affectons uniquement au Button

Exemple:

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

Et utilisez ce style comme thème de votre 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
la source
4

Si vous êtes d'accord avec l'utilisation d'une bibliothèque tierce, consultez traex / RippleEffect . Il vous permet d'ajouter un effet d'entraînement à n'importe quelle vue avec seulement quelques lignes de code. Il vous suffit d'envelopper, dans votre fichier de mise en page XML, l'élément que vous souhaitez avoir un effet d'entraînement avec un com.andexert.library.RippleViewconteneur.

Comme bonus supplémentaire, il nécessite Min SDK 9 afin que vous puissiez avoir une cohérence de conception entre les versions du système d'exploitation.

Voici un exemple tiré du référentiel GitHub des bibliothèques:

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

Vous pouvez modifier la couleur de l'ondulation en ajoutant cet attribut à l'élément RippleView: app:rv_color="@color/my_fancy_ripple_color

guy.gc
la source
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"/>

Utilisation

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

et la couleur sera acceptée sur pré-lolipop

André Bäuml
la source
2

Il y a deux approches expliquées dans le grand tutoriel d'Alex Lockwood: http://www.androiddesignpatterns.com/2016/08/coloring-buttons-with-themeoverlays-background-tints.html :

Approche n ° 1: modification de la couleur d'arrière-plan du bouton avec un 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"/>

Approche n ° 2: définition de la teinte d'arrière-plan de 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
la source
2

Application par programme des couleurs:

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
la source
-1

J'ai essayé ceci:

android:backgroundTint:"@color/mycolor"

au lieu de modifier la propriété d'arrière-plan. Cela ne supprime pas l'effet matériel.

shiladitya
la source
Cela ne fonctionnera pas pour les appareils pré-Lollipop (70% de part de marché). En fait, il a été rétroporté, mais Android Studio affiche toujours des erreurs lors de son utilisation sans le préfixe Android.
Siddharth Pant