Come cambiare i colori di un Drawable in Android?


271

Sto lavorando su un'applicazione Android e ho un disegno che sto caricando da un'immagine di origine. Su questa immagine, vorrei convertire tutti i pixel bianchi in un colore diverso, dire blu e quindi memorizzare nella cache l'oggetto Drawable risultante in modo da poterlo utilizzare in seguito.

Ad esempio, supponiamo che io abbia un file PNG 20x20 con un cerchio bianco nel mezzo e che tutto al di fuori del cerchio sia trasparente. Qual è il modo migliore per rendere blu quel cerchio bianco e memorizzare i risultati nella cache? La risposta cambia se voglio usare quell'immagine sorgente per creare diversi nuovi Drawable (diciamo blu, rosso, verde, arancione, ecc.)?

Immagino che vorrò usare ColorMatrix in qualche modo, ma non sono sicuro di come.


2
L'hai finalmente fatto funzionare in qualche modo? Vedo molte risposte in basso, di cui ho provato anche molte, ma nulla funziona. Al momento ho un quadrato bianco, che vorrei colorare ogni volta in base alle necessità, in modo da non dover creare risorse statiche. Suggerisco, mentre sto ancora aspettando una soluzione funzionante per la mia forma semplice in colore bianco completo.
omkar.ghaisas,

@ omkar.ghaisas Ho creato una libreria chiamata SillyAndroid che contiene una versatile classe di colorazione e fa tutti i tipi di colorazione per disegni e testo. Puoi verificarlo su github.com/milosmns/silly-android . La lezione si trova a/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
milosmns il

Risposte:


221

Penso che tu possa semplicemente usare Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). Ciò imposterebbe i pixel bianchi su rossi, ma non penso che influenzerebbe i pixel trasparenti.

Vedi # setColorFilter Drawable


9
Funzionerà bene quando il disegno è monocolore, meglio quando è bianco.
Mitul Nakum,

67
Se il colore viene modificato dinamicamente (ad esempio in Adattatore), il disegno deve essere modificabile. Esempio: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)maggiori informazioni: curious-creature.org/2009/05/02//munable-mutations
sabato

1
Sì, è particolarmente utile per l'evidenziazione (più chiara, più scura o per aggiungere una tonalità a un'immagine in scala di grigi.) Uso questo trucco per attivare i pulsanti in cui "deselezionato" è in scala di grigi e "controllato" è un colore in grassetto dalla tavolozza dei colori della mia app. Personalmente lo trovo più semplice di una casella di controllo personalizzata.
thom_nic

2
Questo è esattamente quello che stavo cercando, anche se è incredibilmente fastidioso che non possiamo farlo in XML ( tranne su 5.0+ ). La colorazione non è nemmeno disponibile in AppCompat, quindi siamo bloccati a dover chiamare setColorFilterogni volta che utilizziamo le icone invece di avere selettori con tonalità di colore diverso. Tuttavia, è una soluzione molto migliore rispetto alla modifica diretta di png e con risorse statiche aggiuntive.
Chris Cirefice,

21
La moltiplicazione non funzionerà se l'icona della fonte ha un colore scuro. Per dipingere la forma dell'icona della fonte con il colore di destinazione, utilizzare SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Distwo

152

Prova questo codice:

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

106

So che questa domanda è stata posta molto prima di Lollipop, ma vorrei aggiungere un modo carino per farlo su Android 5. +. Fai un disegno XML che fa riferimento a quello originale e imposta la tinta su di esso in questo modo:

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

fa parte anche questa ultima libreria di supporto?
S-K '

No. Questo aiuta solo con alcuni semplici widget.
MinceMan,

8
Tint è in support-v4 tramite DrawableCompat
Mark Renouf,

1
Fantastico lo esaminerò e lo aggiornerò di conseguenza.
MinceMan,

Fresco non supporta questo tipo di drawable
jackqack

62

Il nuovo supporto v4 riporta la tinta su api 4.

puoi farlo in questo modo

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}

2
A partire dalla libreria di supporto 22.
rnrneverdies,

1
Questa è la soluzione preferita, la colorazione dei disegni estraibili è stata un'area grigia nelle vecchie API da quando è stato rilasciato lollipop. Questo frena quella barriera! Non ne sapevo nulla - grazie @Pei
RicardoSousaDev l'

2
Stai attento! È necessario mutare il nuovo drawable "#mutate ()" avvolto per evitare problemi relativi allo stato. Vedi stackoverflow.com/a/44593641/5555218
Ricard

62

Se hai un disegno che è un colore solido e vuoi cambiarlo in un colore solido differnet, puoi usare a ColorMatrixColorFilter. La trasparenza è preservata.

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

3
Se si desidera utilizzare una risorsa colore anziché una stringa (# ff0000 ecc.), È possibile utilizzare ad esempio int iColor = getResources (). GetColor (R.color.primary)
Ben Clayton

funziona ma ho la casella di controllo e voglio conservare il segno di spunta bianco nel mezzo. Qualche suggerimento per quello?
Farooq Arshed

3
Il codice nel commento di Ben è ora deprecato. Invece, puoi usare int iColor = ContextCompat.getColor(context, R.color.primary);.
divieto di geoingegneria il

risposta brillante !! Ringrazia tutti!
Kaveesh Kanwal,

@Mike Hill Ok, spiega perché hai messo più di 20 colori. Devi inserire più di 20 colori nell'array perché altrimenti si blocca con java.lang.ArrayIndexOutOfBoundsException
AlexPad

50

Uso anche ImageViewper le icone (nella ListViewschermata delle impostazioni). Ma penso che ci sia un modo molto più semplice per farlo.

Utilizzare tintper modificare la sovrapposizione di colore sull'icona selezionata.

In XML,

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

funziona bene da quando viene AppCompat


3
Funziona come un fascino! Semplice e perfetto Questo deve essere contrassegnato come risposta accettata.
Naga Mallesh Maddali,

2
Ci sono molte buone risposte qui, ma per la domanda di OP questa è la soluzione migliore e più semplice.
Sebastian Breit,

per api 22 e oltre
philip oghenerobo balogun

1
@philipoghenerobobalogun ho visto questo lavoro su api 19
Jemshit Iskenderov

41

Dovresti farlo per tutte le API:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);

Ciò ha risolto il problema in modo accettabile. Ma quando si filtra il colore, può succedere (è successo a me) che il colore risultante non sia come previsto. Il colore che doveva schiarire. Quello che ho fatto è stato: `nuovo LightingColorFilter (Color.parseColor (" # FF000000 "), myFinalColor)`
Yoraco Gonzales,

1
Sottolineando ciò che penso stia dicendo il commentatore precedente, questa soluzione cambia i colori se i 2 parametri in LightingColorFilter sono diversi, ad esempio, ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY);cambierà il nero in grigio nel disegno.
hBrent,

1
Questo sembra non funzionare quando si usa l'alfa per il colore della tinta.
ypresto,

30

Sono stato in grado di farlo con il seguente codice, che è tratto da un'attività (il layout è molto semplice, contiene solo un ImageView e non è pubblicato qui).

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

da dove ottengo la soglia o il FROM_COLOR?
mikepenz,

Quelle erano solo costanti che ho definito; Ho appena modificato la risposta per includerli.
Matt McMinn,

grazie;) provato ma non si adatta al problema che ho. ho provato setColorFilter e questo funziona ma c'è un problema con il ridimensionamento dell'immagine .9.png. quindi se hai un'idea del perché, rispondi alla mia domanda. stackoverflow.com/questions/5884481/...
mikepenz

1
I filtri colorati sono molto più facili.
afollestad,

17

Puoi risolverlo usando le librerie compat di supporto 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))

1
@AmitabhaBiswas Perché cambi completamente in sbagliato la mia risposta? Parte per parte. 1. getResources (). GetDrawable () è deprecato !! 2. Uso le librerie di supporto perché non voglio preoccuparmi delle versioni di Andorid Api. 3. Non voglio ridisegnare Disegnabile .... Se vuoi mostrare un altro approccio scrivi la tua risposta.
Ricard,

1
@AmitabhaBiswas Inoltre, i drawable sono condivisi tra tutti getDrawable dalle risorse, quindi mutate()è necessaria la chiamata per poter cambiare la tinta di un drawable, senza alterare tutti i drawable associati a quell'ID risorsa.
Ricard,

1
Questa è la risposta migliore! L'avvolgimento del disegno in una vista immagine non risolve la domanda.
Giulio

15

Nella tua attività puoi colorare le tue risorse di immagine PNG con un solo colore:

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

Ora, quando usi il R. Disegno. * Dovrebbe essere colorato con la tinta desiderata. Se hai bisogno di colori aggiuntivi, dovresti essere in grado di .mutate () il disegno.



4

Se hai il tuo set da disegnare su ImageView puoi farlo con 1 liner:

yourImageView.setColorFilter(context.getResources().getColor(R.color.YOUR_COLOR_HERE);

3

Dai un'occhiata a questo codice di esempio " 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 pertinente è disponibile qui :


1
Questo mostra come usare ColorMatrix, ma non vedo come usarlo per ottenere i risultati che sto cercando.
Matt McMinn,

3

Funziona con tutto con lo sfondo:

Textview, Button ...

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

3

Questo frammento di codice ha funzionato per me:

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

imgView.getDrawable().setColorFilter(porterDuffColorFilter);
imgView.setBackgroundColor(Color.TRANSPARENT)

2

Ci sono così tante soluzioni ma nessuno ha suggerito se il file xml della risorsa colore ha già colore, quindi possiamo scegliere direttamente da lì anche come di seguito:

ImageView imageView = (ImageView) findViewById(R.id.imageview);
imageView.setColorFilter(getString(R.color.your_color));

1

Breve esempio per cambiare il colore disegnabile secondo isWorking campo.

La mia forma 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>

Il mio metodo per cambiare:

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

Esempio di utilizzo:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);

0
Int color = Color.GRAY; 
// or int color = Color.argb(123,255,0,5);
// or int color = 0xaaff000;

in XML /res/values/color.xml

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

Codice Java

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

GradientDrawable drawableBg = yourView.getBackground().mutate();
drawableBg.setColor(color);

0

Troppo tardi ma nel caso qualcuno ne abbia bisogno:

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

0

Funziona con alcuni semplici disegni. L'ho usato su una semplice forma rettangolare di colore solido con angoli arrotondati e avevo bisogno di cambiare quel colore con diversi layout.

Prova questo

android:backgroundTint="#101010"

-1

È molto semplice quando usi una libreria per farlo. Prova questo libreria

Puoi chiamare così:

Icon.on(holderView).color(R.color.your_color).icon(R.mipmap.your_icon).put();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.