Définir la couleur de la forme Android par programmation

168

J'édite pour rendre la question plus simple, en espérant que cela aide à une réponse précise.

Disons que j'ai la ovalforme suivante :

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

Comment définir la couleur par programmation, à partir d'une classe d'activités?

Côte Mounyo
la source
À quoi définissez-vous ce dessin?
Vikram
Le dessinable est un ovalet est l'arrière-plan d'un ImageView.
Cote Mounyo
Si cette question posée est trop difficile, existe-t-il un moyen de dessiner plusieurs images sur une toile et de définir le produit final en couches comme arrière-plan d'une vue?
Cote Mounyo
Vous pouvez y parvenir en étendant la Viewclasse et en l'utilisant comme basevue dans une mise en page qui permet le chevauchement des widgets ( RelativeLayout, FrameLayout). Dans cette Viewclasse étendue , vous pouvez draw multiple images onto a canvas. Mais avant de faire cela, jetez un œil à ceci -> Lien (si vous ne l'avez pas déjà fait).
Vikram

Réponses:

266

Remarque : La réponse a été mise à jour pour couvrir le scénario oùbackground trouve une instance de ColorDrawable. Merci Tyler Pfaff , de l'avoir signalé.

Le dessinable est un ovale et est l'arrière-plan d'un ImageView

Obtenez le DrawabledeimageView utilisation getBackground():

Drawable background = imageView.getBackground();

Vérifiez contre les suspects habituels:

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));
}

Version compacte:

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));
}

Notez que la vérification des valeurs nulles n'est pas requise.

Cependant, vous devez utiliser mutate()sur les drawables avant de les modifier s'ils sont utilisés ailleurs. (Par défaut, les dessinables chargés à partir de XML partagent le même état.)

Vikram
la source
3
Merci de répondre. (+1). Mon code connaît d'autres bogues, il est donc difficile à tester. Mais cela pourrait encore définir la solidpartie de la forme. Et la strokeportion?
Cote Mounyo
1
@TiGer Vous devez ajouter @usernamevotre commentaire pour vous assurer qu'une notification est envoyée à l'utilisateur. À propos, vous devrez ShapeDrawablecréer une sous-classe pour définir la partie de trait. Plus d'informations ici: Lien . Regardez le commentaire car il mentionne un problème avec la réponse acceptée.
Vikram
3
android.graphics.drawable.GradientDrawable ne peut pas être casté en android.graphics.drawable.ShapeDrawable Le casting échoue sur moi
John
3
@John Si votre ImageView'sarrière-plan est défini sur a GradientDrawable, getBackground()ne renverra pas de fichier ShapeDrawable. Au lieu de cela, utilisez le GradientDrawablequi est retourné: GradientDrawable gradientDrawable = (GradientDrawable)imageView.getBackground();.... gradientDrawable.setColors(new int[] { color1, color2 });.
Vikram
2
Merci .. a sauvé mon monde.
sid_09
43

Fait comme ça:

    ImageView imgIcon = findViewById(R.id.imgIcon);
    GradientDrawable backgroundGradient = (GradientDrawable)imgIcon.getBackground();
    backgroundGradient.setColor(getResources().getColor(R.color.yellow));
Léonly91
la source
1
@ user3111850 Avez-vous ajouté android:backgroundvotre xml ou même une setBackgroundactivité avant d'appeler getBackground()? Cela devrait fonctionner si vous l'aviez fait.
Lee Yi Hong le
41

Une solution plus simple de nos jours serait d'utiliser votre forme comme arrière-plan, puis de changer sa couleur par programmation via:

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

Voir PorterDuff.Mode pour les options disponibles.

MISE À JOUR (API 29):

La méthode ci-dessus est obsolète depuis l'API 29 et remplacée par la suivante:

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

Voir BlendMode pour les options disponibles.

Georgios
la source
4
La bonne est la suivante: view.getBackground().setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP);Puisqu'il peut y avoir une bordure sur le fond ou des cournes arrondies.
Berkay Turancı
1
Belle @ BerkayTurancı ma forme avait des coins arrondis. Je pourrais omettre l' getBackground()appel. Mon imageview.src contenait une forme et j'ai utilisé: imageIndicator.setColorFilter(toggleColor, PorterDuff.Mode.SRC_ATOP);toggleColorest juste un int qui a précédemment stocké le résultat de getColor ()
Someone Somewhere
1
Avoir PorterDuff.Modepour BlendModeColorFilterne compilera pas comme il se doit BlendMode. Donc, pour l'API 29, cela devrait être view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP).
Onik
Bonne prise @Onik. J'ai mis à jour la réponse en conséquence. Je vous remercie!
Georgios le
14

Essaye ça:

 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);
 }

Pour plus de détails consultez ce lien ce

espérons de l'aide.

androidqq6
la source
vote pour le lien. Mais ce n'est pas la réponse à ma question.
Cote Mounyo
13

j'espère que cela aidera quelqu'un avec le même problème

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
la source
1
Au départ, je ne pouvais pas faire fonctionner cela, j'ai compris que votre couleur devait être fournie comme ceci:gd.setStroke(width_px, Color.parseColor("#FF5722"));
pwnsauce
12

En développant la réponse de Vikram , si vous colorez des vues dynamiques, comme des éléments de vue recycleur, etc. Vous voudrez probablement appeler mutate () avant de définir la couleur. Si vous ne le faites pas, toutes les vues qui ont un dessin commun (c.-à-d. Un arrière-plan) verront également leur dessin modifié / coloré.

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
la source
3
besoins et vérification supplémentaire et paramètre: if (background instanceof LayerDrawable) { background = ((LayerDrawable) background.mutate()).getDrawable(indexIfLayerDrawable); } if (background instanceof ShapeDrawable)[...]pour gérer les dispositions d'arrière-plans qui utilisent <layer-list ... <item ....
Johny
11

Cette question a reçu une réponse il y a quelque temps, mais elle peut être modernisée en la réécrivant en tant que fonction d'extension kotlin.

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

c'est la solution qui fonctionne pour moi ... l'a également écrit dans une autre question: Comment changer la couleur de la forme de manière dynamique?

//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)
Communauté
la source
4

Rien ne fonctionne pour moi, mais lorsque je règle la couleur de la teinte, cela fonctionne sur Shape Drawable

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

nécessite Android 5.0 API 21

Sumit
la source
3

Version de la fonction d'extension My Kotlin basée sur les réponses ci-dessus avec 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
la source
1

Le moyen simple de remplir la forme avec le rayon est:

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

Peut-être suis-je trop tard, mais si vous utilisez Kotlin. Il y a un moyen comme ça

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)

Prendre plaisir....

Système zélé
la source
0

Pour tous ceux qui utilisent C # Xamarin, voici une méthode basée sur l'extrait de code 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
la source