Efeito material no botão com a cor de fundo


246

Estou usando a biblioteca de suporte do Android v21.

Eu criei um botão com a cor de fundo personalizada. Os efeitos de design do material, como ondulação e revelação, desapareceram (exceto a elevação ao clicar) quando uso a cor de fundo do terreno.

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

fundo A seguir, é um botão normal e os efeitos estão funcionando bem.

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

Botão padrão


9
Isso ocorre porque você substitui o plano de fundo por todos os bons efeitos de material por uma cor sólida. Quanto à cor adequada de um botão, mantendo os efeitos do Material, ninguém no SO conseguiu descobrir isso ainda.
— Nathan Walters

2
@SweetWisher A ondulação e outros efeitos não são aplicados no botão quando altero a cor de fundo.
— Sathesh

1
@NathanWalters Mas a cor cinza padrão é feia. :-(
— Sathesh

6
Acredite, eu sei ... Estaremos em grande dívida com quem descobrir como fazer isso corretamente.
— 187 Nathan Walters #

Respostas:


431

Ao usar android:background, você substitui grande parte do estilo e aparência de um botão por uma cor em branco.

Atualização: a partir da versão 23.0.0 do AppCompat , existe um novo Widget.AppCompat.Button.Coloredestilo que usa os temas colorButtonNormalpara a cor desativada e colorAccentpara a cor ativada.

Isso permite que você o aplique diretamente no botão via

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

Se você precisar de um personalizado colorButtonNormalou colorAccent, poderá usar um ThemeOverlaycomo explicado nesta dica profissional e android:themeno botão.

Resposta Anterior

Você pode usar um drawable em seu diretório v21 para o seu plano de fundo, como:

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

Isso garantirá que a cor do plano de fundo seja ?attr/colorPrimarye tenha a animação de ondulação padrão usando o padrão ?attr/colorControlHighlight(que você também pode definir no seu tema, se desejar).

Nota: você precisará criar um seletor personalizado para menos 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>

Supondo que você tenha algumas cores desejadas para o estado padrão, pressionado e focado. Pessoalmente, tirei uma captura de tela de uma onda no meio da seleção e tirei o estado primário / focado disso.


13
Boa resposta! Exatamente o que eu estava procurando, no entanto, precisava limpar e reconstruir antes que o projeto decidisse identificar o drawable no drawable-v21.
— user1354603


4
@ianhanniballake Eu me pergunto como combinar esta técnica de trabalho sem perder cantos arredondados personalização naqueles botão
— Joaquin Iurchuk

3
@ianhanniballake apenas um comentário, drawable em ripple sob <item android: drawable ... /> não pode ser uma cor transparente. Nesse caso, a ondulação não será mostrada. Levei um tempo para perceber isso.
— Ivan Kušt 26/08/15

2
Com appcompat 23.1.1 e API 22, o estado desativado e ativado assumem a cor colorButtonNormal do tema.
— Rémy DAVID

93

Existe outra solução simples para fornecer plano de fundo personalizado para os botões "Planas", mantendo os efeitos "Materiais".

  1. Coloque seu botão no ViewGroup com o fundo desejado definido lá
  2. defina selectableItemBackground do tema atual como plano de fundo para o botão (API> = 11)

ou seja:

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

Pode ser usado para botões simples , funciona na API> = 11 e você terá efeito de ondulação em dispositivos = 21, mantendo os botões regulares antes dos 21 anos até que o AppCompat seja atualizado para suportar ondulações também.

Você também pode usar selectableItemBackgroundBorderless somente para> = 21 botões.


Você literalmente pensou fora da caixa :-) Embora essa seja uma maneira de fazer isso, ela pode aumentar o acoplamento de código e usar estilos seria a maneira recomendada.
— Sathesh

3
Isso cria um botão plano com fundo colorido, sim. Mas o botão criado parece um quadrado, sem cantos arredondados e efeitos de sombra.
— precisa saber é o seguinte

7
@SanketBerde Exatamente. Porque é isso que é o botão "plano".
— precisa saber é o seguinte

2
A pergunta original não mencionou nada. Aliás, se você tiver um pai <CardView> em sua solução em vez de <FrameLayout>, o botão resultante será aumentado e terá sombras. Isso funciona mesmo nas versões pré-pirulito.
— precisa saber é o seguinte

1
@SanketBerde ótimo saber sobre a CardViewabordagem, obrigado por compartilhar!
— Kiruwka

88

Aqui está uma maneira simples e compatível com versões anteriores de fornecer efeito cascata a botões elevados com o plano de fundo personalizado.

Seu layout deve ficar assim

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

9
Esta é a maneira mais fácil e sem complicações de conseguir isso. Boa resposta.
— Tyler Pfaff

1
api 21+, mas não está funcionando. Aplicado no layout linear com fundo clicável e verdadeiro - cor diferente, especificada em primeiro plano.
— 27416 Alex

3
Como Button é um TextView e não um FrameLayout, foregroundnão tem efeito antes de 23. google.com/…
— androidguy

2
LEGAIS! Eu nem sabia que "primeiro plano" era uma coisa!
— Eric Schlenz

2
Funciona como um encanto..!
— 264 Kannan

25

Encontrei esse problema hoje, na verdade, brincando com a biblioteca v22.

Supondo que você esteja usando estilos, você pode definir a colorButtonNormalpropriedade e os botões usarão essa cor por padrão.

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

Fora isso, você ainda pode criar um estilo para o botão e usá-lo por botão se precisar de uma variedade de cores (não testei, apenas especulei).

Lembre-se de adicionar android:antes dos nomes dos itens no seu estilo v21 .


Para obter uma variedade de botões coloridos, consulte a resposta de Robert: stackoverflow.com/a/31825439/1617737
— ban-

Com a cor de fundo agora definida, há também um atributo geral que posso usar no bloco de código acima para especificar a cor do texto do botão?
— ban-geoengineering

24

Com AppCompat (22.1.1+), você pode adicionar um estilo como este:

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

E use-o apenas aplicando o estilo:

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

Alterando programaticamente a cor, descobri que a única maneira de atualizar a cor (na API 15 ou 16) era usar a 'lista de tonalidades de fundo'. E não remove a bela animação radial nos dispositivos da API 21:

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

Porque button.setBackground(...)e button.getBackground().mutate().setColorFilter(...)não altere a cor do botão na API 15 e 16 como na API 21.


1
Muito obrigado, funcionou perfeitamente para mim (mesmo usando Button em layouts XML como AppCompat inflater substitui-lo com AppCompatButton)
— Louis CAD

1
Use ColorStateList.valueOf( ... )para construir um simples ColorStateListde apenas uma cor.
— Taig

colorAccent e rippleColor não funcionam no estilo
— Yar

Obrigado por este post. O que funcionou para mim: ViewCompat.setBackgroundTintList(myButton, ColorStateList.valueOf(myColor); Javadoc para setSupportBackgroundTintListdiz que deve ser assim chamado.
— JerabekJakub

22

A resposta do @ianianniballake é absolutamente correta e simples. Mas levei alguns dias para entender. Para alguém que não entende sua resposta, aqui está uma implementação mais detalhada

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

5
Sua resposta foi a última peça do quebra-cabeça para mim. Pode-se também escrever colorButtonNormalno tema base @style/AppTheme. Isso fornece uma cor padrão ao AppCompat para colorir os botões adequadamente. E então você pode usar sua resposta para criar um tema individualmente.
— Siddharth Pant

1
É android:theme="Theme.MaterialButton"orandroid:theme="@style/Theme.MaterialButton"
— osrl 14/11/16

1
@osrl É android:theme="@style/Theme.MaterialButton". Obrigado pela sua correção.
— Frank Nguyen

12

Eu usei o backgroundTint e o primeiro plano:

<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 não há necessidade para o atributo de primeiro plano como backgroundTint não substitui o ondulação padrão
— Julian Os

7

Eu vim para este post procurando uma maneira de ter uma cor de plano de fundo do meu ListViewitem, mas manter a ondulação.

Simplesmente adicionei minha cor como plano de fundo e o selectableItemBackground como primeiro plano:

<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 funciona como um encanto. Eu acho que a mesma técnica também pode ser usada para botões. Boa sorte :)



6

Se você está interessado no ImageButton, pode tentar o seguinte:

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

5

v22.1de appcompat-v7introduziu algumas novas possibilidades. Agora é possível atribuir um tema específico apenas a uma visualização.

Uso preterido do app: tema para estilizar a Barra de Ferramentas. Agora você pode usar o android: theme para barras de ferramentas em todos os dispositivos API nível 7 e superiores e o suporte android: theme para todos os widgets nos dispositivos API level 11 e superiores.

Portanto, em vez de definir a cor desejada em um tema global, criamos um novo e atribuímos apenas ao Button

Exemplo:

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

E use esse estilo como tema do seu 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 você estiver bem com o uso de uma biblioteca de terceiros, consulte traex / RippleEffect . Permite adicionar um efeito cascata a QUALQUER visualização com apenas algumas linhas de código. Você só precisa agrupar, em seu arquivo de layout xml, o elemento que deseja que tenha um efeito cascata com um com.andexert.library.RippleViewcontêiner.

Como um bônus adicional, requer o SDK 9 mínimo, para que você possa ter consistência de design nas versões do sistema operacional.

Aqui está um exemplo retirado do repositório GitHub das bibliotecas:

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

Você pode alterar a cor da ondulação adicionando esse atributo ao 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"/>

Usar

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

e a cor será aceita no pré-lolipop


2

Há duas abordagens explicadas no ótimo tutorial: Alex Lockwood: http://www.androiddesignpatterns.com/2016/08/coloring-buttons-with-themeoverlays-background-tints.html :

Abordagem 1: Modificando a cor de fundo do botão com um 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"/>

Abordagem nº 2: definindo a tonalidade de segundo plano do 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

Aplicando programaticamente as cores:

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

Eu tentei isso:

android:backgroundTint:"@color/mycolor"

em vez de alterar a propriedade em segundo plano. Isso não remove o efeito material.


Isso não funcionará para dispositivos pré-pirulito (70% de participação de mercado). Na verdade, ele foi suportado, mas o Android Studio ainda mostra erros ao usá-lo sem o prefixo do Android.
— Siddharth Pant
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.