Comment changer les couleurs d'un Drawable dans Android?

271

Je travaille sur une application Android et j'ai un dessin que je charge à partir d'une image source. Sur cette image, j'aimerais convertir tous les pixels blancs en une couleur différente, par exemple le bleu, puis mettre en cache l'objet Drawable résultant afin de pouvoir l'utiliser plus tard.

Par exemple, disons que j'ai un fichier PNG 20x20 qui a un cercle blanc au milieu et que tout ce qui est en dehors du cercle est transparent. Quelle est la meilleure façon de transformer ce cercle blanc en bleu et de mettre en cache les résultats? La réponse change-t-elle si je veux utiliser cette image source pour créer plusieurs nouveaux Drawables (disons bleu, rouge, vert, orange, etc.)?

Je suppose que je veux utiliser un ColorMatrix d'une manière ou d'une autre, mais je ne sais pas comment.

Matt McMinn
la source
2
Avez-vous finalement réussi à faire en sorte que cela fonctionne? Je vois beaucoup de réponses ci-dessous, parmi lesquelles j'en ai essayé beaucoup aussi, mais rien ne fonctionne. J'ai actuellement un carré blanc, que je voudrais colorer à chaque fois en fonction des besoins, pour ne pas avoir à créer d'actifs statiques. Pls suggèrent, car j'attends toujours une solution de travail pour ma forme simple en pleine couleur blanche.
omkar.ghaisas
@ omkar.ghaisas J'ai construit une bibliothèque appelée SillyAndroid qui contient une classe de coloriage polyvalente et fait toutes sortes de coloriages pour les dessins et le texte. Vous pouvez le vérifier sur github.com/milosmns/silly-android . La classe a lieu à/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
milosmns le

Réponses:

221

Je pense que vous pouvez simplement utiliser Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). Cela mettrait les pixels blancs en rouge mais je ne pense pas que cela affecterait les pixels transparents.

Voir Drawable # setColorFilter

thom_nic
la source
9
Cela fonctionnera bien lorsque l'étirable est d'une seule couleur, mieux lorsque son blanc.
Mitul Nakum
67
Si la couleur est changée dinammicaly (par exemple dans l'adaptateur) le drawable doit être mutable. Exemple: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)plus d'infos: curious-creature.org/2009/05/02/drawable-mutations
sabadow
1
Oui, c'est particulièrement bon pour la mise en évidence (plus claire, plus sombre ou pour ajouter une teinte à une image en niveaux de gris.) J'utilise cette astuce pour basculer les boutons où "non coché" est en niveaux de gris et "coché" est une couleur audacieuse de la palette de couleurs de mon application. Personnellement, je trouve cela plus facile qu'une case à cocher personnalisée.
thom_nic
2
C'est exactement ce que je cherchais, bien que ce soit incroyablement ennuyeux que nous ne puissions pas le faire en XML ( sauf pour 5.0+ ). La teinture n'est même pas disponible dans AppCompat, nous sommes donc obligés d'appeler à setColorFilterchaque fois que nous utilisons les icônes au lieu d'avoir des sélecteurs avec des teintes de couleurs différentes. Pourtant, c'est une bien meilleure solution que de modifier directement les pngs et d'avoir des actifs statiques supplémentaires.
Chris Cirefice du
21
La multiplication ne fonctionnera pas si votre icône source a une couleur sombre. Pour peindre la forme de l'icône source avec la couleur de destination, utilisez SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Distwo
152

Essayez ce code:

ImageView lineColorCode = (ImageView)convertView.findViewById(R.id.line_color_code);
int color = Color.parseColor("#AE6118"); //The color u want             
lineColorCode.setColorFilter(color);
Naren
la source
106

Je sais que cette question a été posée bien avant Lollipop mais je voudrais ajouter une belle façon de le faire sur Android 5. +. Vous créez un dessin XML qui fait référence à l'original et définissez la teinte comme ceci:

<?xml version="1.0" encoding="utf-8"?>
<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/ic_back"
    android:tint="@color/red_tint"/>
MinceMan
la source
cette partie de la dernière bibliothèque de support aussi?
S-K '
Non. Cela n'aide que quelques widgets simples.
MinceMan
8
Tint est en support-v4 via DrawableCompat
Mark Renouf
1
Cool, je vais examiner cela et le mettre à jour en conséquence.
MinceMan
Fresco ne prend pas en charge ce type de tirable
jackqack
63

Le nouveau support v4 ramène la teinte à l'API 4.

tu peux le faire comme ça

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}
Pei
la source
2
À partir de la bibliothèque de support 22.
rnrneverdies
1
C'est LA solution préférée, la teinture des tirages a été une zone grise dans les anciennes API depuis la sortie de lollipop. Cela freine cette barrière! Je ne connaissais pas celui-ci - merci @Pei
RicardoSousaDev
2
Faites attention! Vous devez muter votre nouveau "#mutate ()" à tirer, pour éviter les problèmes liés à l'état. Voir stackoverflow.com/a/44593641/5555218
Ricard
62

Si vous avez un dessinable de couleur unie et que vous souhaitez le changer en une couleur unie différente, vous pouvez utiliser un ColorMatrixColorFilter. La transparence est préservée.

int iColor = Color.parseColor(color);

int red   = (iColor & 0xFF0000) / 0xFFFF;
int green = (iColor & 0xFF00) / 0xFF;
int blue  = iColor & 0xFF;

float[] matrix = { 0, 0, 0, 0, red,
                   0, 0, 0, 0, green,
                   0, 0, 0, 0, blue,
                   0, 0, 0, 1, 0 };

ColorFilter colorFilter = new ColorMatrixColorFilter(matrix);
drawable.setColorFilter(colorFilter);
Mike Hill
la source
3
Si vous souhaitez utiliser une ressource de couleur plutôt qu'une chaîne (# ff0000 etc.), vous pouvez utiliser par exemple int iColor = getResources (). GetColor (R.color.primary)
Ben Clayton
cela fonctionne mais j'ai une case à cocher et je veux conserver la coche blanche au milieu. Une suggestion pour ça?
Farooq Arshed
3
Le code dans le commentaire de Ben est désormais obsolète. Au lieu de cela, vous pouvez utiliser int iColor = ContextCompat.getColor(context, R.color.primary);.
ban-geoengineering
réponse brillante !! Merci a tous!
Kaveesh Kanwal
@Mike Hill Ok, expliquez pourquoi vous mettez plus de 20 couleurs.Vous devez insérer plus de vingt couleurs dans le tableau, sinon cela se bloque avec java.lang.ArrayIndexOutOfBoundsException
AlexPad
50

J'utilise également ImageViewdes icônes (dans ListViewou écran des paramètres). Mais je pense qu'il existe un moyen beaucoup plus simple de le faire.

Utilisez tintpour modifier la superposition de couleurs sur l'icône sélectionnée.

En xml,

android:tint="@color/accent"
android:src="@drawable/ic_event" 

fonctionne bien car il vient de AppCompat

sud007
la source
3
Fonctionne comme un charme! Simple et parfait. Cela doit être marqué comme réponse acceptée.
Naga Mallesh Maddali
2
Il y a beaucoup de bonnes réponses ici, mais pour la question d'OP, c'est la meilleure et la plus simple solution.
Sebastian Breit
pour api 22 et supérieur
philip oghenerobo balogun
1
@philipoghenerobobalogun j'ai vu cela fonctionner sur l'api 19
Jemshit Iskenderov
41

Vous devez le faire pour toutes les API:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);
hoangtu23
la source
Cela a résolu le problème d'une manière acceptable. Mais lors du filtrage de la couleur, il peut arriver (cela m'est arrivé) que la couleur résultante ne soit pas celle attendue. La couleur qui allait s'éclaircir. Ce que j'ai fait était: `nouveau LightingColorFilter (Color.parseColor (" # FF000000 "), myFinalColor)`
Yoraco Gonzales
1
Soulignant ce que je pense que le commentateur précédent dit, cette solution change les couleurs si les 2 paramètres du LightingColorFilter sont différents, par exemple, ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY);changeront le noir en gris dans le dessinable.
hBrent
1
Cela ne semble pas fonctionner lorsque l'alpha est utilisé pour la couleur de la teinte.
ypresto
30

J'ai pu le faire avec le code suivant, qui est tiré d'une activité (la mise en page est très simple, contenant simplement une ImageView, et n'est pas publiée ici).

private static final int[] FROM_COLOR = new int[]{49, 179, 110};
private static final int THRESHOLD = 3;

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.test_colors);

    ImageView iv = (ImageView) findViewById(R.id.img);
    Drawable d = getResources().getDrawable(RES);
    iv.setImageDrawable(adjust(d));
}

private Drawable adjust(Drawable d)
{
    int to = Color.RED;

    //Need to copy to ensure that the bitmap is mutable.
    Bitmap src = ((BitmapDrawable) d).getBitmap();
    Bitmap bitmap = src.copy(Bitmap.Config.ARGB_8888, true);
    for(int x = 0;x < bitmap.getWidth();x++)
        for(int y = 0;y < bitmap.getHeight();y++)
            if(match(bitmap.getPixel(x, y))) 
                bitmap.setPixel(x, y, to);

    return new BitmapDrawable(bitmap);
}

private boolean match(int pixel)
{
    //There may be a better way to match, but I wanted to do a comparison ignoring
    //transparency, so I couldn't just do a direct integer compare.
    return Math.abs(Color.red(pixel) - FROM_COLOR[0]) < THRESHOLD &&
        Math.abs(Color.green(pixel) - FROM_COLOR[1]) < THRESHOLD &&
        Math.abs(Color.blue(pixel) - FROM_COLOR[2]) < THRESHOLD;
}
Matt McMinn
la source
d'où puis-je obtenir le seuil ou le FROM_COLOR?
mikepenz
Ce ne sont que des constantes que j'ai définies; Je viens de modifier la réponse pour les inclure.
Matt McMinn
merci;) essayé mais cela ne correspond pas au problème que j'ai. essayé le setColorFilter, et cela fonctionne mais il y a un problème avec la mise à l'échelle de l'image .9.png. donc si vous avez une idée pourquoi, veuillez répondre à ma question. stackoverflow.com/questions/5884481/…
mikepenz
1
Les filtres de couleur sont beaucoup plus faciles.
afollestad
17

Vous pouvez le résoudre en utilisant les bibliothèques de compatibilité Android. :)

 // mutate to not share its state with any other drawable
 Drawable drawableWrap = DrawableCompat.wrap(drawable).mutate();
 DrawableCompat.setTint(drawableWrap, ContextCompat.getColor(getContext(), R.color.your_color))
Ricard
la source
1
@AmitabhaBiswas Pourquoi changez-vous complètement pour me tromper de réponse? Partie par partie. 1. getResources (). GetDrawable () est déconseillé !! 2. J'utilise des bibliothèques de support car je ne veux pas me soucier des versions d'Andorid Api. 3. Je ne veux pas redessiner Drawable .... Si vous voulez montrer une autre approche, écrivez votre propre réponse.
Ricard
1
@AmitabhaBiswas En outre, les drawables sont partagés entre tous les getDrawable des ressources, par conséquent, l' mutate()appel est nécessaire pour pouvoir changer la teinte d'un drawable, sans modifier tous les drawables associés à cet ID de ressource.
Ricard
1
C'est la meilleure réponse! L'emballage dessinable dans une vue d'image ne résout pas la question.
Julius
15

Dans votre activité, vous pouvez colorer vos ressources d'image PNG avec une seule couleur:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    myColorTint();
    setContentView(R.layout.activity_main);
}

private void myColorTint() {
    int tint = Color.parseColor("#0000FF"); // R.color.blue;
    PorterDuff.Mode mode = PorterDuff.Mode.SRC_ATOP;
    // add your drawable resources you wish to tint to the drawables array...
    int drawables[] = { R.drawable.ic_action_edit, R.drawable.ic_action_refresh };
    for (int id : drawables) {
        Drawable icon = getResources().getDrawable(id);
        icon.setColorFilter(tint,mode);
    }
}

Maintenant, lorsque vous utilisez le R.drawable. * Il doit être coloré avec la teinte souhaitée. Si vous avez besoin de couleurs supplémentaires, vous devriez pouvoir .mutate () le dessinable.

David Douglas
la source
4
view.getDrawable().mutate().setColorFilter(0xff777777, PorterDuff.Mode.MULTIPLY); 

Merci à @sabadow

Hamidreza Sadegh
la source
setColorFilter déconseillé
Mohammad Sommakia
4

Si vous avez votre ensemble dessinable sur ImageView, vous pouvez le faire avec une doublure:

yourImageView.setColorFilter(context.getResources().getColor(R.color.YOUR_COLOR_HERE);
Martin Nowosad
la source
3

Découvrez cet exemple de code " ColorMatrixSample.java "

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.apis.graphics;

import com.example.android.apis.R;

import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public class ColorMatrixSample extends GraphicsActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new SampleView(this));
    }

    private static class SampleView extends View {
        private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private ColorMatrix mCM = new ColorMatrix();
        private Bitmap mBitmap;
        private float mSaturation;
        private float mAngle;

        public SampleView(Context context) {
            super(context);

            mBitmap = BitmapFactory.decodeResource(context.getResources(),
                                                   R.drawable.balloons);
        }

        private static void setTranslate(ColorMatrix cm, float dr, float dg,
                                         float db, float da) {
            cm.set(new float[] {
                   2, 0, 0, 0, dr,
                   0, 2, 0, 0, dg,
                   0, 0, 2, 0, db,
                   0, 0, 0, 1, da });
        }

        private static void setContrast(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   scale, 0, 0, 0, translate,
                   0, scale, 0, 0, translate,
                   0, 0, scale, 0, translate,
                   0, 0, 0, 1, 0 });
        }

        private static void setContrastTranslateOnly(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   1, 0, 0, 0, translate,
                   0, 1, 0, 0, translate,
                   0, 0, 1, 0, translate,
                   0, 0, 0, 1, 0 });
        }

        private static void setContrastScaleOnly(ColorMatrix cm, float contrast) {
            float scale = contrast + 1.f;
               float translate = (-.5f * scale + .5f) * 255.f;
            cm.set(new float[] {
                   scale, 0, 0, 0, 0,
                   0, scale, 0, 0, 0,
                   0, 0, scale, 0, 0,
                   0, 0, 0, 1, 0 });
        }

        @Override protected void onDraw(Canvas canvas) {
            Paint paint = mPaint;
            float x = 20;
            float y = 20;

            canvas.drawColor(Color.WHITE);

            paint.setColorFilter(null);
            canvas.drawBitmap(mBitmap, x, y, paint);

            ColorMatrix cm = new ColorMatrix();

            mAngle += 2;
            if (mAngle > 180) {
                mAngle = 0;
            }

            //convert our animated angle [-180...180] to a contrast value of [-1..1]
            float contrast = mAngle / 180.f;

            setContrast(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x + mBitmap.getWidth() + 10, y, paint);

            setContrastScaleOnly(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x, y + mBitmap.getHeight() + 10, paint);

            setContrastTranslateOnly(cm, contrast);
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(mBitmap, x, y + 2*(mBitmap.getHeight() + 10),
                              paint);

            invalidate();
        }
    }
}

L'API correspondante est disponible ici :

Adrian
la source
1
Cela montre comment utiliser ColorMatrix, mais je ne vois pas comment l'utiliser pour obtenir les résultats que je recherche.
Matt McMinn
3

Cela fonctionne avec tout ce qui a un arrière-plan:

Affichage de texte, bouton ...

TextView text = (TextView) View.findViewById(R.id.MyText);
text.setBackgroundResource(Icon);    
text.getBackground().setColorFilter(getResources().getColor(Color), PorterDuff.Mode.SRC_ATOP);
toni
la source
3

Cet extrait de code a fonctionné pour moi:

PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(getResources().getColor(R.color.your_color),PorterDuff.Mode.MULTIPLY);

imgView.getDrawable().setColorFilter(porterDuffColorFilter);
imgView.setBackgroundColor(Color.TRANSPARENT)
Mehatab
la source
2

Il y a tellement de solutions mais personne n'a suggéré que si le fichier xml de ressource couleur a déjà une couleur, nous pouvons choisir directement à partir de là aussi comme ci-dessous:

ImageView imageView = (ImageView) findViewById(R.id.imageview);
imageView.setColorFilter(getString(R.color.your_color));
Pankaj
la source
1

Petit exemple pour changer la couleur de dessin selon isWorking champ.

Ma forme xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
    <solid android:color="@android:color/holo_blue_bright" />
    <corners android:radius="30dp" />
    <size
        android:height="15dp"
        android:width="15dp" />
</shape>

Ma méthode pour changer:

private Drawable getColoredDrawable(int drawableResId, boolean isworking) {
    Drawable d = getResources().getDrawable(R.drawable.shape);
    ColorFilter filter = new LightingColorFilter(
            isworking ? Color.GREEN : Color.RED,
            isworking ? Color.GREEN : Color.RED);
    d.setColorFilter(filter);
    return d;
}

Exemple d'utilisation:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);
poisson mort
la source
0
Int color = Color.GRAY; 
// or int color = Color.argb(123,255,0,5);
// or int color = 0xaaff000;

en XML /res/values/color.xml

<?xml version="1.0" encoding="utf-8">
<resources>
    <color name="colorRed">#ff0000</color>
</resoures> 

Code Java

int color = ContextCompat.getColor(context, R.color.colorRed);

GradientDrawable drawableBg = yourView.getBackground().mutate();
drawableBg.setColor(color);
Ven Ren
la source
0

Trop tard mais au cas où quelqu'un en aurait besoin:

   fun setDrawableColor(drawable: Drawable, color: Int) :Drawable {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            drawable.colorFilter = BlendModeColorFilter(color, BlendMode.SRC_ATOP)
            return drawable
        } else {
            drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
            return drawable
        }
    }
Mouaad Abdelghafour AITALI
la source
0

Cela fonctionne pour certains drawables simples. Je l'ai utilisé sur une simple forme rect en couleur unie avec des coins arrondis et j'avais besoin de changer cette couleur avec différentes dispositions.

Essaye ça

android:backgroundTint="#101010"
Jay Parikh
la source
-1

C'est très très simple lorsque vous utilisez une bibliothèque pour le faire pour vous. Essayez cette bibliothèque

Vous pouvez appeler comme ceci:

Icon.on(holderView).color(R.color.your_color).icon(R.mipmap.your_icon).put();
Vansuita Jr.
la source