studio - material theme android




Botón estándar de Android con un color diferente (12)

Me gustaría cambiar el color de un botón estándar de Android para que coincida mejor con la marca de un cliente.

La mejor manera que he encontrado para hacer esto hasta ahora es cambiar el res/drawable/red_button.xml del Button al dibujable ubicado en res/drawable/red_button.xml :

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

Pero hacer eso requiere que en realidad cree tres elementos dibujables diferentes para cada botón que quiero personalizar (uno para el botón en reposo, uno cuando está enfocado y otro cuando se presiona). Eso parece más complicado y no SECO de lo que necesito.

Todo lo que realmente quiero hacer es aplicar algún tipo de transformación de color al botón. ¿Hay una manera más fácil de cambiar el color de un botón que lo que estoy haciendo?


Úsalo de esta manera:

buttonOBJ.getBackground().setColorFilter(Color.parseColor("#YOUR_HEX_COLOR_CODE"), PorterDuff.Mode.MULTIPLY);

A partir de la respuesta de Tomasz, también puede establecer mediante programación el sombreado de todo el botón utilizando el modo de multiplicación de PorterDuff. Esto cambiará el color del botón en lugar de solo el tinte.

Si comienzas con un botón sombreado gris estándar:

button.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);

te dará un botón rojo sombreado,

button.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);

le dará un botón verde sombreado, etc., donde el primer valor es el color en formato hexadecimal.

Funciona al multiplicar el valor de color actual del botón por su valor de color. Estoy seguro de que también hay mucho más que puedes hacer con estos modos.


Ahora también puede usar el AppCompatButton de appcompat-v7 con el atributo backgroundTint :

<android.support.v7.widget.AppCompatButton
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:backgroundTint="#ffaa00"/>

Descubrí que todo esto se puede hacer en un archivo con bastante facilidad. Coloque algo como el siguiente código en un archivo llamado custom_button.xml y luego establezca background="@drawable/custom_button" en su vista de botón:

<?xml version="1.0" encoding="utf-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true" >
        <shape>
            <gradient
                android:startColor="@color/yellow1"
                android:endColor="@color/yellow2"
                android:angle="270" />
            <stroke
                android:width="3dp"
                android:color="@color/grey05" />
            <corners
                android:radius="3dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>

    <item android:state_focused="true" >
        <shape>
            <gradient
                android:endColor="@color/orange4"
                android:startColor="@color/orange5"
                android:angle="270" />
            <stroke
                android:width="3dp"
                android:color="@color/grey05" />
            <corners
                android:radius="3dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>

    <item>        
        <shape>
            <gradient
                android:endColor="@color/blue2"
                android:startColor="@color/blue25"
                android:angle="270" />
            <stroke
                android:width="3dp"
                android:color="@color/grey05" />
            <corners
                android:radius="3dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>
</selector>

Esta es mi solución que funciona perfectamente a partir de la API 15 . Esta solución mantiene todos los efectos de clic de botón predeterminados, como el material RippleEffect . No lo he probado en API más bajas, pero debería funcionar.

Todo lo que necesitas hacer, es:

1) Crea un estilo que cambie solo colorAccent :

<style name="Facebook.Button" parent="ThemeOverlay.AppCompat">
    <item name="colorAccent">@color/com_facebook_blue</item>
</style>

Recomiendo usar ThemeOverlay.AppCompat o su AppTheme principal como padre, para mantener el resto de sus estilos.

2) Agregue estas dos líneas a su widget de button :

style="@style/Widget.AppCompat.Button.Colored"
android:theme="@style/Facebook.Button"

A veces, su nuevo colorAccent no se muestra en la vista previa de Android Studio, pero cuando inicie su aplicación en el teléfono, el color cambiará.

Widget de botón de muestra

<Button
    android:id="@+id/sign_in_with_facebook"
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="@string/sign_in_facebook"
    android:textColor="@android:color/white"
    android:theme="@style/Facebook.Button" />


Estoy usando este enfoque

style.xml

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:colorPrimaryDark">#413152</item>
    <item name="android:colorPrimary">#534364</item>
    <item name="android:colorAccent">#534364</item>
    <item name="android:buttonStyle">@style/MyButtonStyle</item>
</style>

<style name="MyButtonStyle" parent="Widget.AppCompat.Button.Colored">
    <item name="android:colorButtonNormal">#534364</item>
    <item name="android:textColor">#ffffff</item>
</style>

Como puede ver desde arriba, estoy usando un estilo personalizado para mi botón. El color del botón corresponde al color del acento. Considero que este es un enfoque mucho mejor que configurar android:background ya que no perderé el efecto dominó de Google.


La forma en que hago un botón de estilo diferente que funciona bastante bien es subclasificar el objeto Button y aplicar un filtro de color. Esto también maneja los estados habilitados y deshabilitados aplicando un alfa al botón.

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.Button;

public class DimmableButton extends Button {

    public DimmableButton(Context context) {
        super(context);
    }

    public DimmableButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public DimmableButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @SuppressWarnings("deprecation")
    @Override
    public void setBackgroundDrawable(Drawable d) {
        // Replace the original background drawable (e.g. image) with a LayerDrawable that
        // contains the original drawable.
        DimmableButtonBackgroundDrawable layer = new DimmableButtonBackgroundDrawable(d);
        super.setBackgroundDrawable(layer);
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void setBackground(Drawable d) {
        // Replace the original background drawable (e.g. image) with a LayerDrawable that
        // contains the original drawable.
        DimmableButtonBackgroundDrawable layer = new DimmableButtonBackgroundDrawable(d);
        super.setBackground(layer);
    }

    /**
     * The stateful LayerDrawable used by this button.
     */
    protected class DimmableButtonBackgroundDrawable extends LayerDrawable {

        // The color filter to apply when the button is pressed
        protected ColorFilter _pressedFilter = new LightingColorFilter(Color.LTGRAY, 1);
        // Alpha value when the button is disabled
        protected int _disabledAlpha = 100;
        // Alpha value when the button is enabled
        protected int _fullAlpha = 255;

        public DimmableButtonBackgroundDrawable(Drawable d) {
            super(new Drawable[] { d });
        }

        @Override
        protected boolean onStateChange(int[] states) {
            boolean enabled = false;
            boolean pressed = false;

            for (int state : states) {
                if (state == android.R.attr.state_enabled)
                    enabled = true;
                else if (state == android.R.attr.state_pressed)
                    pressed = true;
            }

            mutate();
            if (enabled && pressed) {
                setColorFilter(_pressedFilter);
            } else if (!enabled) {
                setColorFilter(null);
                setAlpha(_disabledAlpha);
            } else {
                setColorFilter(null);
                setAlpha(_fullAlpha);
            }

            invalidateSelf();

            return super.onStateChange(states);
        }

        @Override
        public boolean isStateful() {
            return true;
        }
    }

}

Me gusta la sugerencia de filtro de color en respuestas anteriores de @conjugatedirection y @Tomasz; Sin embargo, encontré que el código provisto hasta ahora no se aplicó tan fácilmente como esperaba.

Primero, no se mencionó dónde aplicar y borrar el filtro de color. Es posible que haya otros buenos lugares para hacer esto, pero lo que me vino a la mente fue un OnTouchListener .

De mi lectura de la pregunta original, la solución ideal sería una que no involucre ninguna imagen. La respuesta aceptada utilizando custom_button.xml de @emmby es probablemente una mejor opción que los filtros de color, si ese es su objetivo. En mi caso, estoy empezando con una imagen png de un diseñador de UI de cómo se supone que debe ser el botón. Si configuro el fondo del botón para esta imagen, la retroalimentación de resaltado predeterminada se pierde completamente. Este código reemplaza ese comportamiento con un efecto de oscurecimiento programático.

button.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 0x6D6D6D sets how much to darken - tweak as desired
                setColorFilter(v, 0x6D6D6D);
                break;
            // remove the filter when moving off the button
            // the same way a selector implementation would 
            case MotionEvent.ACTION_MOVE:
                Rect r = new Rect();
                v.getLocalVisibleRect(r);
                if (!r.contains((int) event.getX(), (int) event.getY())) {
                    setColorFilter(v, null);
                }
                break;
            case MotionEvent.ACTION_OUTSIDE:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                setColorFilter(v, null);
                break;
        }
        return false;
    }

    private void setColorFilter(View v, Integer filter) {
        if (filter == null) v.getBackground().clearColorFilter();
        else {
            // To lighten instead of darken, try this:
            // LightingColorFilter lighten = new LightingColorFilter(0xFFFFFF, filter);
            LightingColorFilter darken = new LightingColorFilter(filter, 0x000000);
            v.getBackground().setColorFilter(darken);
        }
        // required on Android 2.3.7 for filter change to take effect (but not on 4.0.4)
        v.getBackground().invalidateSelf();
    }
});

Extraje esto como una clase separada para la aplicación a varios botones, que se muestra como clase interna anónima solo para tener una idea.


Puede configurar el tema de su botón para este

<style name="AppTheme.ButtonBlue" parent="Widget.AppCompat.Button.Colored">
 <item name="colorButtonNormal">@color/HEXColor</item>
 <item name="android:textColor">@color/HEXColor</item>
</style>

Si está creando botones de color con XML, puede hacer que el código sea un poco más limpio especificando el estado enfocado y presionado en un archivo separado y reutilizándolos. Mi botón verde se ve así:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_focused="true" android:drawable="@drawable/button_focused"/>
    <item android:state_pressed="true" android:drawable="@drawable/button_pressed"/>

    <item>
        <shape>
            <gradient android:startColor="#ff00ff00" android:endColor="#bb00ff00" android:angle="270" />
            <stroke android:width="1dp" android:color="#bb00ff00" />
            <corners android:radius="3dp" />
            <padding android:left="10dp" android:top="10dp" android:right="10dp" android:bottom="10dp" />
        </shape>
    </item>

</selector>

Una forma fácil es simplemente definir una clase de botón personalizada que acepte todas las propiedades que desee, como radio, degradado, color pulsado, color normal, etc., y luego use eso en sus diseños XML en lugar de configurar el fondo utilizando XML. Una muestra esta here

Esto es extremadamente útil si tiene muchos botones con las mismas propiedades como radio, color seleccionado, etc. Puede personalizar su botón heredado para manejar estas propiedades adicionales.

Resultado (no se utilizó selector de fondo).

Botón normal

Botón presionado


valores \ styles.xml

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

<style name="RedAccentButton" parent="ThemeOverlay.AppCompat.Light">
    <item name="colorAccent">#ff0000</item>
</style>

entonces:

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

<Button
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:enabled="false"
    android:text="text" />

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

<Button
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:enabled="false"
    android:text="text"
    android:theme="@style/RedAccentButton" />





android-layout