Wie ändere ich die Farben eines Drawable in Android?

271

Ich arbeite an einer Android-Anwendung und habe eine Zeichnung, die ich von einem Quellbild lade. Auf diesem Bild möchte ich alle weißen Pixel in eine andere Farbe konvertieren, z. B. Blau, und dann das resultierende zeichnbare Objekt zwischenspeichern, damit ich es später verwenden kann.

Angenommen, ich habe eine 20x20-PNG-Datei mit einem weißen Kreis in der Mitte und alles außerhalb des Kreises ist transparent. Was ist der beste Weg, um diesen weißen Kreis blau zu machen und die Ergebnisse zwischenzuspeichern? Ändert sich die Antwort, wenn ich dieses Quellbild verwenden möchte, um mehrere neue Drawables zu erstellen (z. B. blau, rot, grün, orange usw.)?

Ich vermute, dass ich eine ColorMatrix irgendwie verwenden möchte, bin mir aber nicht sicher, wie.

Matt McMinn
quelle
2
Hast du das endlich irgendwie zum Laufen gebracht? Ich sehe unten viele Antworten, von denen ich auch viele ausprobiert habe, aber nichts funktioniert. Ich habe derzeit ein weißes Quadrat, das ich je nach Bedarf jedes Mal anders färben möchte, damit ich keine statischen Assets erstellen muss. Bitte schlagen Sie vor, da ich immer noch auf eine funktionierende Lösung für meine einfache Form in voller weißer Farbe warte.
Omkar.ghaisas
@ omkar.ghaisas Ich habe eine Bibliothek namens SillyAndroid erstellt, die eine vielseitige Farbklasse enthält und alle Arten von Farben für Zeichen und Text ausführt. Sie können es unter github.com/milosmns/silly-android überprüfen . Die Klasse befindet sich am/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
1.

Antworten:

221

Ich denke, Sie können tatsächlich nur verwenden Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). Dies würde weiße Pixel auf Rot setzen, aber ich glaube nicht, dass dies die transparenten Pixel beeinflussen würde.

Siehe Drawable # setColorFilter

thom_nic
quelle
9
Dies funktioniert gut, wenn das Zeichen einfarbig ist, besser, wenn es weiß ist.
Mitul Nakum
67
Wenn die Farbe geändert wird (z. B. im Adapter), muss das Zeichenelement veränderbar sein. Beispiel: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)Weitere Informationen: neugierig-creature.org/2009/05/02/drawable-mutations
sabadow
1
Ja, es eignet sich besonders zum Hervorheben (heller, dunkler oder Hinzufügen eines Farbtons zu einem Graustufenbild). Mit diesem Trick schalte ich Schaltflächen um, bei denen "nicht markiert" Graustufen ist und "aktiviert" eine fette Farbe aus der Farbpalette meiner App ist. Persönlich finde ich es einfacher als ein benutzerdefiniertes Kontrollkästchen.
thom_nic
2
Dies ist genau das, wonach ich gesucht habe, obwohl es unglaublich ärgerlich ist, dass wir dies nicht in XML tun können ( außer bei 5.0+ ). Das Abtönen ist in AppCompat nicht einmal verfügbar, daher müssen wir nicht setColorFilterjedes Mal aufrufen, wenn wir die Symbole verwenden, anstatt Selektoren mit unterschiedlichen Farbtönen zu haben. Trotzdem ist es eine viel bessere Lösung, als PNGs direkt zu bearbeiten und zusätzliche statische Assets zu haben.
Chris Cirefice
21
Multiplizieren funktioniert nicht, wenn Ihr Quellensymbol eine dunkle Farbe hat. Um die Form des Quellensymbols mit der SRC_INmyImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Zielfarbe
152

Probieren Sie diesen Code aus:

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

Ich weiß, dass diese Frage schon lange vor Lollipop gestellt wurde, aber ich möchte eine nette Möglichkeit hinzufügen, dies auf Android 5. + zu tun. Sie machen eine XML-Zeichnung, die auf die ursprüngliche verweist, und setzen die Farbe wie folgt darauf:

<?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
quelle
Ist dies auch Teil der neuesten Support-Bibliothek?
S-K
Nein, das hilft nur mit ein paar einfachen Widgets.
MinceMan
8
Tint ist in Support-v4 über DrawableCompat
Mark Renouf
1
Cool, ich werde das untersuchen und dies entsprechend aktualisieren.
MinceMan
Fresco unterstützt diese Art von Drawable nicht
Jackqack
62

Die neue Unterstützung v4 bringt den Farbton zurück zu API 4.

Sie können es so machen

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}
Pei
quelle
2
Ausgehend von der Support-Bibliothek 22.
rnrneverdies
1
Dies ist DIE bevorzugte Lösung. Das Abtönen von Drawables war in älteren APIs seit der Veröffentlichung von Lollipop eine Grauzone. Dies bremst diese Barriere! Ich wusste nichts davon - danke @Pei
RicardoSousaDev
2
Achtung! Sie sollten Ihr neues verpacktes Drawable "#mutate ()" mutieren, um zustandsbezogene Probleme zu vermeiden. Siehe stackoverflow.com/a/44593641/5555218
Ricard
62

Wenn Sie eine Zeichenfläche haben, die einfarbig ist, und Sie sie in eine andere einfarbige Farbe ändern möchten, können Sie eine verwenden ColorMatrixColorFilter. Die Transparenz bleibt erhalten.

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
quelle
3
Wenn Sie eine Farbressource anstelle einer Zeichenfolge (# ff0000 usw.) verwenden möchten, können Sie z. B. int iColor = getResources () verwenden. GetColor (R.color.primary)
Ben Clayton
Dies funktioniert, aber ich habe ein Kontrollkästchen und möchte das weiße Häkchen in der Mitte beibehalten. Irgendein Vorschlag dafür?
Farooq Arshed
3
Der Code in Bens Kommentar ist jetzt veraltet. Stattdessen können Sie verwenden int iColor = ContextCompat.getColor(context, R.color.primary);.
Ban-Geoengineering
geniale Antwort !! Vielen Dank an alle!
Kaveesh Kanwal
@ Mike Hill Ok, erklären Sie, warum Sie mehr als 20 Farben eingefügt haben. Sie müssen mehr als zwanzig Farben in das Array einfügen, da es sonst mit java.lang.ArrayIndexOutOfBoundsException
AlexPad am
50

Ich benutze auch ImageViewfür Symbole (im ListViewoder Einstellungsbildschirm). Aber ich denke, es gibt einen viel einfacheren Weg, das zu tun.

Verwenden Sie tintdiese Option , um die Farbüberlagerung für das ausgewählte Symbol zu ändern.

In XML,

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

funktioniert gut, da es von kommt AppCompat

sud007
quelle
3
Klappt wunderbar! Einfach und perfekt. Dies muss als akzeptierte Antwort markiert werden.
Naga Mallesh Maddali
2
Hier gibt es viele gute Antworten, aber für die Frage von OP ist dies die beste und einfachste Lösung.
Sebastian Breit
für api 22 und höher
philip oghenerobo balogun 31.
1
@phipiphenhenobobalogun Ich sah diese Arbeit an API 19
Jemshit Iskenderov
41

Sie sollten dies für alle APIs tun:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);
hoangtu23
quelle
Dies löste das Problem auf akzeptable Weise. Beim Filtern der Farbe kann es jedoch vorkommen (mir ist es passiert), dass die resultierende Farbe nicht den Erwartungen entspricht. Die Farbe, die aufhellen sollte. Was ich getan habe war: `neuer LightingColorFilter (Color.parseColor (" # FF000000 "), myFinalColor)`
Yoraco Gonzales
1
Diese Lösung betont, was meiner Meinung nach der vorherige Kommentator sagt, und ändert die Farben, wenn ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY);die beiden Parameter im LightingColorFilter unterschiedlich sind, z. B. ändert sich Schwarz in Grau im Zeichenobjekt.
hBrent
1
Dies scheint nicht zu funktionieren, wenn Alpha für die Farbtonfarbe verwendet wird.
Ypresto
30

Ich konnte dies mit dem folgenden Code tun, der aus einer Aktivität stammt (das Layout ist sehr einfach, enthält nur eine ImageView und wird hier nicht veröffentlicht).

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
quelle
Woher bekomme ich den Schwellenwert oder den FROM_COLOR?
Mikepenz
Das waren nur Konstanten, die ich definiert habe; Ich habe gerade die Antwort bearbeitet, um sie einzuschließen.
Matt McMinn
danke;) versucht, aber es passt nicht zu dem Problem, das ich habe. habe den setColorFilter ausprobiert, und das funktioniert, aber es gibt ein Problem beim Skalieren des .9.png-Bildes. Wenn Sie eine Idee haben, warum, beantworten Sie bitte meine Frage. stackoverflow.com/questions/5884481/…
mikepenz
1
Farbfilter sind viel einfacher.
Afollestad
17

Sie können es mit kompatiblen Bibliotheken für Android-Unterstützung lösen. :) :)

 // 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
quelle
1
@AmitabhaBiswas Warum änderst du meine Antwort komplett auf falsch? Teil für Teil. 1. getResources (). GetDrawable () ist veraltet !! 2. Ich verwende Support-Bibliotheken, weil ich mich nicht um Andorid Api-Versionen kümmern möchte. 3. Ich möchte Drawable nicht neu zeichnen. Wenn Sie einen anderen Ansatz zeigen möchten, schreiben Sie Ihre eigene Antwort.
Ricard
1
@AmitabhaBiswas Außerdem werden die Drawables von allen getDrawable-Ressourcen gemeinsam genutzt. Daher ist der mutate()Aufruf erforderlich, um den Farbton eines Drawables ändern zu können, ohne alle mit dieser Ressourcen-ID verknüpften Drawables zu ändern.
Ricard
1
Das ist die beste Antwort! Das Einwickeln von Zeichen in eine Bildansicht löst die Frage nicht.
Julius
15

In Ihrer Aktivität können Sie Ihre PNG-Bildressourcen mit einer einzigen Farbe tönen:

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

Wenn Sie jetzt das R.drawable. * Verwenden, sollte es mit dem gewünschten Farbton gefärbt sein. Wenn Sie zusätzliche Farben benötigen, sollten Sie in der Lage sein, das Zeichenobjekt zu mutieren ().

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

Danke an @sabadow

Hamidreza Sadegh
quelle
setColorFilter veraltet
Mohammad Sommakia
4

Wenn Sie Ihr Drawable auf ImageView eingestellt haben, können Sie dies mit einem 1-Liner tun:

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

Schauen Sie sich diesen Beispielcode " ColorMatrixSample.java " an.

/*
 * 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();
        }
    }
}

Die entsprechende API finden Sie hier :

Adrian
quelle
1
Dies zeigt zwar, wie ColorMatrix verwendet wird, aber ich sehe nicht, wie ich es verwenden kann, um die gewünschten Ergebnisse zu erzielen.
Matt McMinn
3

Dies funktioniert mit allem mit Hintergrund:

Textansicht, Button ...

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

Dieses Code-Snippet hat bei mir funktioniert:

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

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

Es gibt so viele Lösungen, aber niemand hat vorgeschlagen, wenn die XML- Datei der Farbressource bereits Farbe hat, können wir direkt von dort auch wie folgt auswählen:

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

Kurzes Beispiel zum Ändern der Zeichenfarbe je nach isWorkingFeld.

Meine Form 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>

Meine Methode zu ändern:

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

Anwendungsbeispiel:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);
toter Fisch
quelle
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> 

Java Code

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

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

Zu spät, aber falls jemand es braucht:

   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
quelle
0

Es funktioniert für einige einfache Drawables. Ich habe es auf einer einfachen einfarbigen, geraden Form mit abgerundeten Ecken verwendet und musste diese Farbe mit verschiedenen Layouts ändern.

Versuche dies

android:backgroundTint="#101010"
Jay Parikh
quelle
-1

Es ist sehr, sehr einfach, wenn Sie eine Bibliothek verwenden, um dies für Sie zu tun. Versuche dies Bibliothek aus

Sie können so anrufen:

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