Jak zmienić kolory Drawable w Androidzie?

271

Pracuję nad aplikacją na Androida i mam rysownicę, którą ładuję z obrazu źródłowego. Na tym zdjęciu chciałbym przekonwertować wszystkie białe piksele na inny kolor, powiedzmy niebieski, a następnie buforować wynikowy obiekt do rysowania, aby móc go później użyć.

Powiedzmy na przykład, że mam plik PNG 20x20, który ma białe kółko pośrodku i że wszystko poza nim jest przezroczyste. Jak najlepiej zmienić to białe koło na niebieskie i zapisać wyniki w pamięci podręcznej? Czy odpowiedź zmienia się, jeśli chcę użyć tego obrazu źródłowego do utworzenia kilku nowych Drawables (np. Niebieski, czerwony, zielony, pomarańczowy itp.)?

Zgaduję, że chcę w jakiś sposób użyć ColorMatrix, ale nie jestem pewien, jak to zrobić.

Matt McMinn
źródło
2
Czy w końcu udało Ci się to w jakiś sposób? Na dole widzę wiele odpowiedzi, z których wiele próbowałem, ale nic nie działa. Obecnie mam biały kwadrat, który chciałbym pokolorować za każdym razem w zależności od potrzeb, aby nie musiałem tworzyć statycznych zasobów. Pls sugerują, ponieważ wciąż czekam na działające rozwiązanie dla mojego prostego kształtu w pełnym białym kolorze.
omkar.ghaisas
@ omkar.ghaisas Zbudowałem bibliotekę o nazwie SillyAndroid, która zawiera wszechstronną klasę Kolorowanki i wykonuje wszelkiego rodzaju kolorowanki dla rysunków i tekstu. Możesz to sprawdzić na github.com/milosmns/silly-android . Klasa znajduje się w/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
milosmns

Odpowiedzi:

221

Myślę, że właściwie możesz po prostu użyć Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). Ustawiłoby to białe piksele na czerwone, ale nie sądzę, żeby wpłynęło to na przezroczyste piksele.

Zobacz Drawable # setColorFilter

thom_nic
źródło
9
Będzie to działało dobrze, gdy rysowany jest w jednym kolorze, lepiej, gdy jest biały.
Mitul Nakum
67
Jeśli kolor zostanie zmieniony dynamicznie (na przykład w adapterze), dający się rysować musi być zmienny. Przykład: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)więcej informacji: curious-creature.org/2009/05/02/drawable-mutations
sabadow
1
Tak, jest to szczególnie dobre do podświetlania (jaśniejsze, ciemniejsze lub dodawanie odcienia do obrazu w skali szarości.) Używam tej sztuczki, aby przełączać przyciski, w których „niezaznaczone” jest w skali szarości, a „zaznaczone” to odważny kolor z palety kolorów mojej aplikacji. Osobiście uważam, że jest to łatwiejsze niż niestandardowe pole wyboru.
thom_nic
2
Właśnie tego szukałem, chociaż jest to niezwykle denerwujące, że nie możemy tego zrobić w XML ( z wyjątkiem wersji 5.0+ ). Barwienie nie jest nawet dostępne w AppCompat, więc utknęliśmy na obowiązku dzwonienia za setColorFilterkażdym razem, gdy korzystamy z ikon, zamiast selektorów o różnych odcieniach kolorów. Jest to jednak znacznie lepsze rozwiązanie niż bezpośrednia edycja plików PNG i posiadanie dodatkowych zasobów statycznych.
Chris Cirefice,
21
Mnożenie nie będzie działać, jeśli ikona źródła ma ciemny kolor. Aby pomalować kształt ikony źródłowej kolorem docelowym, użyj SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Distwo
152

Wypróbuj ten kod:

ImageView lineColorCode = (ImageView)convertView.findViewById(R.id.line_color_code);
int color = Color.parseColor("#AE6118"); //The color u want             
lineColorCode.setColorFilter(color);
Naren
źródło
106

Wiem, że to pytanie było zadawane na długo przed Lollipopem, ale chciałbym dodać fajny sposób, aby to zrobić na Androidzie 5. +. Tworzysz rysunek XML, który odwołuje się do oryginalnego, i ustawia odcień w taki sposób:

<?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
źródło
czy to także część najnowszej biblioteki wsparcia?
S-K '
Nie. Pomaga to tylko w kilku prostych widgetach.
MinceMan
8
Tint jest obsługiwany przez v4 poprzez DrawableCompat
Mark Renouf
1
Fajnie, przyjrzę się temu i odpowiednio zaktualizuję.
MinceMan,
Fresco nie obsługuje tego rodzaju dającego się wyciągnąć
jackqack
62

Nowe wsparcie v4 przywraca odcień do interfejsu API 4.

możesz to zrobić w ten sposób

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}
Pei
źródło
2
Począwszy od biblioteki wsparcia 22.
rnrneverdies
1
Jest to preferowane rozwiązanie, ponieważ od czasu wydania Lollipopa barwy rysunkowe były szarym obszarem w starszych interfejsach API. To hamuje tę barierę! Nie wiedziałem o tym - dzięki @Pei
RicardoSousaDev
2
Bądź ostrożny! Powinieneś zmutować swój nowy owinięty „#mutate ()” do rysowania, aby uniknąć problemów związanych ze stanem. Zobacz stackoverflow.com/a/44593641/5555218
Ricard
62

Jeśli masz rysunek, który ma jednolity kolor i chcesz go zmienić na inny, jednolity kolor, możesz użyć ColorMatrixColorFilter. Przejrzystość jest zachowana.

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
źródło
3
Jeśli chcesz użyć zasobu koloru zamiast ciągu (# ff0000 itp.), Możesz użyć np. Int iColor = getResources (). GetColor (R.color.primary)
Ben Clayton
to działa, ale mam pole wyboru i chcę zachować biały tik na środku. Jakieś sugestie?
Farooq Arshed
3
Kod w komentarzu Bena jest już nieaktualny. Zamiast tego możesz użyć int iColor = ContextCompat.getColor(context, R.color.primary);.
zakaz geoinżynierii
genialna odpowiedź !! Dziękuje wszystkim!
Kaveesh Kanwal
@ Mike Hill Ok, wyjaśnij, dlaczego umieściłeś więcej niż 20 kolorów. Musisz wstawić więcej niż dwadzieścia kolorów w tablicy, ponieważ inaczej ulega awarii z java.lang.ArrayIndexOutOfBoundsException
AlexPad
50

Używam również ImageViewdo ikon (na ListViewekranie ustawień lub). Ale myślę, że istnieje o wiele prostszy sposób na zrobienie tego.

Użyj, tintaby zmienić nakładkę koloru na wybraną ikonę.

W xml

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

działa dobrze, ponieważ pochodzi AppCompat

sud007
źródło
3
Działa jak marzenie! Prosty i idealny. To musi być oznaczone jako zaakceptowana odpowiedź.
Naga Mallesh Maddali
2
Jest tu wiele dobrych odpowiedzi, ale w przypadku pytania OP jest to najlepsze i najprostsze rozwiązanie.
Sebastian Breit
dla interfejsu API w wersji 22 i nowszej
philip oghenerobo balogun
1
@philipoghenerobobalogun widziałem, jak to działa na API 19
Jemshit Iskenderov
41

Powinieneś to zrobić dla wszystkich interfejsów API:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);
hoangtu23
źródło
To rozwiązało problem w akceptowalny sposób. Ale podczas filtrowania koloru może się zdarzyć (zdarzyło mi się), że uzyskany kolor nie jest zgodny z oczekiwaniami. Kolor, który miał rozjaśnić. To, co zrobiłem, to: `new LightingColorFilter (Color.parseColor (" # FF000000 "), myFinalColor)`
Yoraco Gonzales
1
Podkreślając to, co myślę poprzedni komentator, to rozwiązanie zmienia kolory, jeśli 2 parametry w LightingColorFilter są różne, np ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY);. Zmieni kolor czarny na szary w rysunku.
hBrent
1
Wydaje się, że to nie działa, gdy alfa jest używany do koloru odcienia.
ypresto
30

Udało mi się to zrobić za pomocą następującego kodu, który jest pobierany z działania (układ jest bardzo prosty, zawiera tylko ImageView i nie jest tutaj zamieszczany).

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
źródło
skąd wziąć próg lub FROM_COLOR?
mikepenz
To były tylko stałe, które zdefiniowałem; Właśnie zredagowałem odpowiedź, aby je uwzględnić.
Matt McMinn
dziękuję;) próbowałem, ale to nie pasuje do mojego problemu. wypróbowałem setColorFilter i to działa, ale jest problem ze skalowaniem obrazu .9.png. więc jeśli masz pomysł, dlaczego, proszę odpowiedz na moje pytanie. stackoverflow.com/questions/5884481/…
mikepenz
1
Filtry kolorowe są znacznie łatwiejsze.
afollestad
17

Możesz go rozwiązać za pomocą bibliotek kompatybilnych z Androidem. :)

 // 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
źródło
1
@AmitabhaBiswas Dlaczego całkowicie zmieniłeś się, aby źle odpowiedzieć na moją odpowiedź? Kawałek po kawałku. 1. getResources (). GetDrawable () jest przestarzałe !! 2. Korzystam z bibliotek wsparcia, ponieważ nie chcę dbać o wersje Andorid API. 3. Nie chcę przerysowywać Drawable .... Jeśli chcesz pokazać inne podejście, napisz własną odpowiedź.
Ricard,
1
@AmitabhaBiswas Poza tym, drawable są wspólne dla wszystkich getDrawable z zasobów, dlatego mutate()wywołanie jest wymagane, aby móc zmienić odcień drawable, bez zmiany wszystkich powiązań drawable z tym identyfikatorem zasobu.
Ricard,
1
To najlepsza odpowiedź! Zawijanie rysowania w widoku obrazu nie rozwiązuje problemu.
Julius
15

W swojej działalności możesz zabarwić zasoby obrazu PNG jednym kolorem:

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

Teraz, gdy używasz R..drawable. *, Powinien być zabarwiony pożądanym odcieniem. Jeśli potrzebujesz dodatkowych kolorów, powinieneś być w stanie .mutate () rysować.

David Douglas
źródło
4
view.getDrawable().mutate().setColorFilter(0xff777777, PorterDuff.Mode.MULTIPLY); 

Dzięki @sabadow

Hamidreza Sadegh
źródło
przestarzałe setColorFilter
Mohammad Sommakia
4

Jeśli masz zestaw do rysowania w ImageView, możesz to zrobić za pomocą 1 wkładki:

yourImageView.setColorFilter(context.getResources().getColor(R.color.YOUR_COLOR_HERE);
Martin Nowosad
źródło
3

Sprawdź ten przykładowy kod „ 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();
        }
    }
}

Odpowiednie API jest dostępne tutaj :

Adrian
źródło
1
To pokazuje, jak korzystać z ColorMatrix, ale nie widzę, jak go używać, aby uzyskać wyniki, których szukam.
Matt McMinn,
3

Działa to ze wszystkim w tle:

Widok tekstu, przycisk ...

TextView text = (TextView) View.findViewById(R.id.MyText);
text.setBackgroundResource(Icon);    
text.getBackground().setColorFilter(getResources().getColor(Color), PorterDuff.Mode.SRC_ATOP);
toni
źródło
3

Ten fragment kodu działał dla mnie:

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

imgView.getDrawable().setColorFilter(porterDuffColorFilter);
imgView.setBackgroundColor(Color.TRANSPARENT)
Mehatab
źródło
2

Istnieje tak wiele rozwiązań, ale nikt nie sugerował, że jeśli plik XML zasobu kolorów ma już kolor, możemy wybrać bezpośrednio z niego również, jak poniżej:

ImageView imageView = (ImageView) findViewById(R.id.imageview);
imageView.setColorFilter(getString(R.color.your_color));
Pankaj
źródło
1

Krótki przykład zmiany koloru do rysowania według isWorking pola.

Mój kształt 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>

Moja metoda zmiany:

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

Przykład użycia:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);
martwa ryba
źródło
0
Int color = Color.GRAY; 
// or int color = Color.argb(123,255,0,5);
// or int color = 0xaaff000;

w pliku XML /res/values/color.xml

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

Kod Java

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

GradientDrawable drawableBg = yourView.getBackground().mutate();
drawableBg.setColor(color);
Ven Ren
źródło
0

Za późno, ale na wypadek, gdyby ktoś tego potrzebował:

   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
źródło
0

Działa z niektórymi prostymi rysowaniami. Użyłem go na prostym prostym jednolitym kolorze z zaokrąglonymi narożnikami i potrzebowałem zmienić ten kolor za pomocą różnych układów.

Spróbuj tego

android:backgroundTint="#101010"
Jay Parikh
źródło
-1

To bardzo proste, gdy używasz biblioteki, aby to zrobić za Ciebie. Spróbuj tego bibliotekę

Możesz zadzwonić w ten sposób:

Icon.on(holderView).color(R.color.your_color).icon(R.mipmap.your_icon).put();
Vansuita Jr.
źródło