Como alterar as cores de um Drawable no Android?

271

Estou trabalhando em um aplicativo Android e tenho um drawable que estou carregando de uma imagem de origem. Nesta imagem, gostaria de converter todos os pixels brancos em uma cor diferente, digamos azul, e depois armazenar em cache o objeto Drawable resultante para que eu possa usá-lo mais tarde.

Por exemplo, digamos que eu tenha um arquivo PNG de 20x20 que tenha um círculo branco no meio e que tudo fora do círculo seja transparente. Qual é a melhor maneira de transformar esse círculo branco em azul e armazenar em cache os resultados? A resposta muda se eu quiser usar essa imagem de origem para criar vários novos Drawables (por exemplo, azul, vermelho, verde, laranja etc.)?

Acho que vou querer usar um ColorMatrix de alguma forma, mas não sei como.

Matt McMinn
fonte
2
Você finalmente conseguiu isso de alguma maneira? Vejo muitas respostas abaixo, das quais tentei muitas também, mas nada funciona. Atualmente, tenho um quadrado branco, que eu gostaria de colorir sempre de acordo com a necessidade, para não precisar criar ativos estáticos. Pls sugerir, como eu ainda estou esperando por uma solução de trabalho para a minha forma simples na cor branca.
Omkar.ghaisas
@ omkar.ghaisas Criei uma biblioteca chamada SillyAndroid, que contém uma versátil aula de coloração e faz todo tipo de coloração para desenhos e textos. Você pode conferir em github.com/milosmns/silly-android . A classe está localizada em/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
milosmns

Respostas:

221

Eu acho que você pode realmente apenas usar Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). Isso definiria pixels brancos para vermelho, mas não acho que afetaria os pixels transparentes.

Consulte Drawable # setColorFilter

thom_nic
fonte
9
Isso funcionará bem quando o drawable for de cor única, melhor quando for branco.
Mitul Nakum
67
Se a cor for alterada dinamicamente (por exemplo, em Adaptador), o drawable deve ser mutável. Exemplo: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)mais informações: curious-creature.org/2009/05/02/drawable-mutations
sabadow 26/03
1
Sim, é especialmente bom para realçar (mais claro, mais escuro ou adicionar uma tonalidade a uma imagem em escala de cinza). Eu uso esse truque para alternar botões onde "desmarcada" está em escala de cinza e "marcada" é uma cor ousada na paleta de cores do meu aplicativo. Pessoalmente, acho mais fácil do que uma caixa de seleção personalizada.
Thom_nic
2
Isso é exatamente o que eu estava procurando, embora seja incrivelmente irritante que não possamos fazer isso em XML ( exceto no 5.0 ou superior ). A coloração nem está disponível no AppCompat, por isso não podemos ligar setColorFiltersempre que usamos os ícones em vez de ter seletores com tonalidades de cores diferentes. Ainda assim, é uma solução muito melhor do que editar pngs diretamente e ter recursos estáticos extras.
Chris Cirefice
21
A multiplicação não funcionará se o ícone de origem tiver uma cor escura. Para pintar a forma do ícone de origem com a cor de destino, use SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Distwo
152

Experimente este código:

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

Eu sei que esta pergunta foi feita antes do Lollipop, mas gostaria de adicionar uma boa maneira de fazer isso no Android 5. +. Você cria um desenho xml que faz referência ao original e o define como:

<?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
fonte
também faz parte da biblioteca de suporte mais recente?
S-K '
Não. Isso ajuda apenas com alguns widgets simples.
MinceMan
8
Matiz é em apoio-v4 via DrawableCompat
Mark Renouf
1
Legal, vou analisar isso e atualizar isso de acordo.
MinceMan
Não Fresco não suporta este tipo de drawable
jackqack
62

O novo suporte v4 traz o tom de volta à API 4.

você pode fazer assim

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}
Pei
fonte
2
A partir da biblioteca de suporte 22.
rnrneverdies
1
Esta é a solução preferida, o desenho de drawables tem sido uma área cinzenta nas APIs mais antigas desde que o pirulito foi lançado. Isso freia essa barreira! Eu não sabia sobre este - graças @Pei
RicardoSousaDev
2
Seja cuidadoso! Você deve alterar seu novo desenho empacotável "#mutate ()" para evitar problemas relacionados ao estado. Veja stackoverflow.com/a/44593641/5555218
Ricard
62

Se você tem um desenho que é uma cor sólida e deseja alterá-lo para uma cor sólida diferente, você pode usar a ColorMatrixColorFilter. A transparência é preservada.

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
fonte
3
Se você quiser usar um recurso de cor ao invés de uma string (# ff0000 etc), você pode usar por exemplo int iColor = getResources () getColor (R.color.primary).
Ben Clayton
isso funciona, mas eu tenho a caixa de seleção e quero preservar o carrapato branco no meio. Alguma sugestão para isso?
Farooq Arshed
3
O código no comentário de Ben agora está obsoleto. Em vez disso, você pode usar int iColor = ContextCompat.getColor(context, R.color.primary);.
ban-geoengineering
resposta brilhante !! Obrigado a todos!
Kaveesh Kanwal
@ Mike Colina Ok, explicar por que você colocar mais de 20 colors.You precisa inserir greather de vinte cores na matriz porque senão ele bate com java.lang.ArrayIndexOutOfBoundsException
AlexPad
50

Também uso ImageViewpara ícones (na ListViewtela ou configurações). Mas acho que há uma maneira muito mais simples de fazer isso.

Use tintpara alterar a sobreposição de cores no ícone selecionado.

Em xml,

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

funciona bem, uma vez que vem AppCompat

sud007
fonte
3
Funciona como um encanto! Simples e perfeito. Isso precisa ser marcado como resposta aceita.
Naga Mallesh Maddali
2
Existem muitas respostas boas aqui, mas para a pergunta do OP, essa é a melhor e mais simples solução.
Sebastian Breit
para api 22 e acima
philip oghenerobo balogun
1
@philipoghenerobobalogun eu vi isso funcionando na API 19
Jemshit Iskenderov
41

Você deve fazer isso para todas as APIs:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);
hoangtu23
fonte
Isso resolveu o problema de maneira aceitável. Mas, ao filtrar a cor, pode acontecer (aconteceu comigo) que a cor resultante não seja a esperada. A cor que deveria clarear. O que fiz foi: `new LightingColorFilter (Color.parseColor (" # FF000000 "), myFinalColor)`
Yoraco Gonzales
1
Enfatizando o que o comentarista anterior está dizendo, esta solução altera as cores se os 2 parâmetros no LightingColorFilter forem diferentes, por exemplo, ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY);mudará de preto para cinza no drawable.
Hbrent
1
Isso parece não funcionar quando o alfa é usado para a cor da tonalidade.
ypresto 11/07
30

Consegui fazer isso com o código a seguir, retirado de uma atividade (o layout é muito simples, contendo apenas um ImageView e não é publicado aqui).

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
fonte
de onde obtenho o limite ou o FROM_COLOR?
Mikepenz
Essas eram apenas constantes que eu defini; Acabei de editar a resposta para incluí-los.
Matt McMinn
obrigado;) tentei, mas não se encaixa no problema que tenho. tentei o setColorFilter, e isso funciona, mas há um problema com o dimensionamento da imagem .9.png. por isso, se você tiver uma ideia do porquê, responda à minha pergunta. stackoverflow.com/questions/5884481/…
mikepenz
1
Os filtros de cores são muito mais fáceis.
Afollestad
17

Você pode resolvê-lo usando as bibliotecas compatíveis do 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
fonte
1
@AmitabhaBiswas Por que você muda completamente para a minha resposta errada? Parte por parte. 1. getResources (). GetDrawable () está obsoleto !! 2. Uso bibliotecas de suporte porque não quero me preocupar com as versões do Andorid Api. 3. Não quero redesenhar Drawable .... Se você quiser mostrar outra abordagem, escreva sua própria resposta.
Ricard
1
@AmitabhaBiswas Além disso, os drawables são compartilhados entre todos os recursos getDrawable, portanto, a mutate()chamada é necessária para poder alterar a tonalidade de um drawable, sem alterar todos os drawables associados a esse ID de recurso.
Ricard
1
Esta é a melhor resposta! O empacotamento do desenho em uma visualização de imagem não resolve a questão.
Julius
15

Na sua Atividade, você pode colorir os recursos da imagem PNG com uma única cor:

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

Agora, quando você usa o R.drawable. *, Ele deve ser colorido com a tonalidade desejada. Se você precisar de cores adicionais, poderá ativar () a gaveta.

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

Obrigado a @sabadow

Hamidreza Sadegh
fonte
setColorFilter preterido
Mohammad Sommakia
4

Se você tiver seu drawable definido para o ImageView, poderá fazê-lo com um liner 1:

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

Confira este código de exemplo " 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();
        }
    }
}

A API relevante está disponível aqui :

Adrian
fonte
1
Isso mostra como usar o ColorMatrix, mas não estou vendo como usá-lo para obter os resultados que estou procurando.
Matt McMinn
3

Isso funciona com tudo com background:

Visualização de texto, botão ...

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

Este trecho de código funcionou para mim:

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

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

Existem tantas soluções, mas ninguém sugeriu que, se o arquivo xml do recurso de cores já tiver cores, podemos escolher diretamente a partir daí também como abaixo:

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

Exemplo curto para alterar a cor desenhável de acordo com isWorking campo.

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

Meu método para mudar:

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

Exemplo de uso:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);
peixe morto
fonte
0
Int color = Color.GRAY; 
// or int color = Color.argb(123,255,0,5);
// or int color = 0xaaff000;

em XML /res/values/color.xml

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

Código Java

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

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

Tarde demais, mas no caso de alguém precisar:

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

Funciona para alguns drawables simples. Usei-o em uma forma simples de cor sólida com cantos arredondados e precisava mudar essa cor com diferentes layouts.

Tente isto

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

É muito, muito simples, quando você usa uma biblioteca para fazer isso por você. Tente isto biblioteca

Você pode ligar assim:

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