Establecer el color de la forma de Android mediante programación

168

Estoy editando para simplificar la pregunta, con la esperanza de que ayude a obtener una respuesta precisa.

Digamos que tengo la siguiente ovalforma:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:angle="270"
           android:color="#FFFF0000"/>
    <stroke android:width="3dp"
            android:color="#FFAA0055"/>
</shape>

¿Cómo configuro el color mediante programación, desde una clase de actividad?

Cote Mounyo
fuente
¿A qué lo configuras?
Vikram
El dibujable es ovaly es el fondo de un ImageView.
Cote Mounyo
Si esta pregunta es demasiado difícil, ¿hay alguna forma de dibujar varias imágenes en un lienzo y establecer el producto final en capas como fondo de una vista?
Cote Mounyo
Puede lograr esto extendiendo la Viewclase y usándola como basevista en un diseño que permita la superposición de widgets ( RelativeLayout, FrameLayout). Dentro de esta Viewclase extendida , puedes draw multiple images onto a canvas. Pero, antes de hacerlo, eche un vistazo a esto -> Enlace (si aún no lo ha hecho).
Vikram

Respuestas:

266

Nota : La respuesta se ha actualizado para cubrir el escenario donde backgroundhay una instancia de ColorDrawable. Gracias Tyler Pfaff , por señalar esto.

El dibujo es un óvalo y es el fondo de un ImageView

Obtener el Drawablede imageViewusar getBackground():

Drawable background = imageView.getBackground();

Verificar con los sospechosos habituales:

if (background instanceof ShapeDrawable) {
    // cast to 'ShapeDrawable'
    ShapeDrawable shapeDrawable = (ShapeDrawable) background;
    shapeDrawable.getPaint().setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof GradientDrawable) {
    // cast to 'GradientDrawable'
    GradientDrawable gradientDrawable = (GradientDrawable) background;
    gradientDrawable.setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof ColorDrawable) {
    // alpha value may need to be set again after this call
    ColorDrawable colorDrawable = (ColorDrawable) background;
    colorDrawable.setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
}

Versión compacta:

Drawable background = imageView.getBackground();
if (background instanceof ShapeDrawable) {
    ((ShapeDrawable)background).getPaint().setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof GradientDrawable) {
    ((GradientDrawable)background).setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof ColorDrawable) {
    ((ColorDrawable)background).setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
}

Tenga en cuenta que no se requiere verificación nula.

Sin embargo, debe usar mutate()los elementos dibujables antes de modificarlos si se usan en otro lugar. (De forma predeterminada, los elementos extraíbles cargados desde XML comparten el mismo estado).

Vikram
fuente
3
Gracias por responder. (+1) Mi código está experimentando otros errores, por lo que es difícil de probar. Pero aún así esto podría establecer la solidparte de la forma. ¿Qué tal la strokeporción?
Cote Mounyo
1
@TiGer Debe agregar @usernamesu comentario para asegurarse de que se envíe una notificación al usuario. Por cierto, deberás subclasificar ShapeDrawablepara establecer la parte del trazo. Más información aquí: Enlace . Mire el comentario ya que menciona un problema con la respuesta aceptada.
Vikram
3
android.graphics.drawable.GradientDrawable no se puede lanzar a android.graphics.drawable.ShapeDrawable El elenco falla en mí
John
3
@John Si su ImageView'sfondo está configurado en a GradientDrawable, getBackground()no devolverá a ShapeDrawable. En su lugar, utilice el GradientDrawableque se devuelve: GradientDrawable gradientDrawable = (GradientDrawable)imageView.getBackground();.... gradientDrawable.setColors(new int[] { color1, color2 });.
Vikram
2
Gracias ... salvó mi mundo.
sid_09
43

Hazlo así:

    ImageView imgIcon = findViewById(R.id.imgIcon);
    GradientDrawable backgroundGradient = (GradientDrawable)imgIcon.getBackground();
    backgroundGradient.setColor(getResources().getColor(R.color.yellow));
Leonly91
fuente
1
@ user3111850 ¿Agregaste android:backgroundtu xml o incluso setBackgrounden la actividad antes de llamar getBackground()? Debería funcionar si lo hubiera hecho.
Lee Yi Hong
41

Una solución más simple hoy en día sería usar su forma como fondo y luego cambiar su color mediante programación mediante:

view.background.setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP)

Consulte PorterDuff.Mode para ver las opciones disponibles.

ACTUALIZACIÓN (API 29):

El método anterior está en desuso desde API 29 y se reemplaza por lo siguiente:

view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP)

Ver BlendMode para ver las opciones disponibles.

Georgios
fuente
44
La correcta es: view.getBackground().setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP);ya que puede haber un borde en el fondo o correos redondeados.
Berkay Turancı
1
Nice one @ BerkayTurancı mi forma tenía esquinas redondeadas. Podría omitir la getBackground()llamada. Mi imageview.src contenía una forma y utilicé: imageIndicator.setColorFilter(toggleColor, PorterDuff.Mode.SRC_ATOP);where toggleColores solo un int que previamente almacenó el resultado de getColor ()
Someone Somewhere
1
Tener PorterDuff.Modepara BlendModeColorFilterno se compilará como se requiere BlendMode. Entonces para API 29 debería ser view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP).
Onik
Buena captura @Onik. He actualizado la respuesta en consecuencia. ¡Gracias!
Georgios
14

Prueba esto:

 public void setGradientColors(int bottomColor, int topColor) {
 GradientDrawable gradient = new GradientDrawable(Orientation.BOTTOM_TOP, new int[]  
 {bottomColor, topColor});
 gradient.setShape(GradientDrawable.RECTANGLE);
 gradient.setCornerRadius(10.f);
 this.setBackgroundDrawable(gradient);
 }

para más detalles revise este enlace este

Espero ayuda.

androidqq6
fuente
arriba votar por el enlace. Pero no es la respuesta a mi pregunta.
Cote Mounyo
13

espero que esto ayude a alguien con el mismo problema

GradientDrawable gd = (GradientDrawable) YourImageView.getBackground();
//To shange the solid color
gd.setColor(yourColor)

//To change the stroke color
int width_px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, youStrokeWidth, getResources().getDisplayMetrics());
gd.setStroke(width_px, yourColor);
medhdj
fuente
1
Inicialmente no pude hacer que esto funcionara, descubrí que tu Color debe proporcionarse de esta manera:gd.setStroke(width_px, Color.parseColor("#FF5722"));
pwnsauce
12

Ampliando la respuesta de Vikram , si está coloreando vistas dinámicas, como elementos de vista de reciclador, etc. Entonces probablemente quiera llamar a mutate () antes de establecer el color. Si no hace esto, cualquier vista que tenga un dibujo común (es decir, un fondo) también cambiará / coloreará su dibujo.

public static void setBackgroundColorAndRetainShape(final int color, final Drawable background) {

    if (background instanceof ShapeDrawable) {
        ((ShapeDrawable) background.mutate()).getPaint().setColor(color);
    } else if (background instanceof GradientDrawable) {
        ((GradientDrawable) background.mutate()).setColor(color);
    } else if (background instanceof ColorDrawable) {
        ((ColorDrawable) background.mutate()).setColor(color);
    }else{
        Log.w(TAG,"Not a valid background type");
    }

}
Tyler Pfaff
fuente
3
necesidades y verificación adicional y parámetro: if (background instanceof LayerDrawable) { background = ((LayerDrawable) background.mutate()).getDrawable(indexIfLayerDrawable); } if (background instanceof ShapeDrawable)[...]para manejar los diseños de fondos que usan <layer-list ... <item ....
Johny
11

Esta pregunta fue respondida hace un tiempo, pero se puede modernizar reescribiendo como una función de extensión kotlin.

fun Drawable.overrideColor(@ColorInt colorInt: Int) {
    when (this) {
        is GradientDrawable -> setColor(colorInt)
        is ShapeDrawable -> paint.color = colorInt
        is ColorDrawable -> color = colorInt
    }
}
Carlos Paulino
fuente
7

esta es la solución que funciona para mí ... también la escribí en otra pregunta: ¿Cómo cambiar dinámicamente el color de la forma?

//get the image button by id
ImageButton myImg = (ImageButton) findViewById(R.id.some_id);

//get drawable from image button
GradientDrawable drawable = (GradientDrawable) myImg.getDrawable();

//set color as integer
//can use Color.parseColor(color) if color is a string
drawable.setColor(color)
Comunidad
fuente
4

Nada funciona para mí, pero cuando configuro el color de tinte funciona en Shape Drawable

 Drawable background = imageView.getBackground();
 background.setTint(getRandomColor())

requiere Android 5.0 API 21

Sumit
fuente
3

Mi versión de la función de extensión Kotlin basada en las respuestas anteriores con Compat :

fun Drawable.overrideColor_Ext(context: Context, colorInt: Int) {
    val muted = this.mutate()
    when (muted) {
        is GradientDrawable -> muted.setColor(ContextCompat.getColor(context, colorInt))
        is ShapeDrawable -> muted.paint.setColor(ContextCompat.getColor(context, colorInt))
        is ColorDrawable -> muted.setColor(ContextCompat.getColor(context, colorInt))
        else -> Log.d("Tag", "Not a valid background type")
    }
}
Sattar Hummatli
fuente
1

La manera simple de llenar la forma con el Radio es:

(view.getBackground()).setColorFilter(Color.parseColor("#FFDE03"), PorterDuff.Mode.SRC_IN);
SANAT
fuente
1

Quizás sea demasiado tarde, pero si estás usando Kotlin. Hay una manera como esta

var gd = layoutMain.background as GradientDrawable

 //gd.setCornerRadius(10)
  gd.setColor(ContextCompat.getColor(ctx , R.color.lightblue))
  gd.setStroke(1, ContextCompat.getColor(ctx , R.color.colorPrimary)) // (Strokewidth,colorId)

Disfrutar....

Sistema celoso
fuente
0

Para cualquiera que use C # Xamarin, aquí hay un método basado en el fragmento de Vikram:

private void SetDrawableColor(Drawable drawable, Android.Graphics.Color color)
{
    switch (drawable)
    {
        case ShapeDrawable sd:
            sd.Paint.Color = color;
            break;
        case GradientDrawable gd:
            gd.SetColor(color);
            break;
        case ColorDrawable cd:
            cd.Color = color;
            break;
    }
}
Ryan
fuente