¿Cómo cambiar los colores de un Drawable en Android?

271

Estoy trabajando en una aplicación de Android, y tengo un dibujo que estoy cargando desde una imagen de origen. En esta imagen, me gustaría convertir todos los píxeles blancos a un color diferente, digamos azul, y luego almacenar en caché el objeto Drawable resultante para poder usarlo más tarde.

Entonces, por ejemplo, digamos que tengo un archivo PNG de 20x20 que tiene un círculo blanco en el medio, y que todo lo que está fuera del círculo es transparente. ¿Cuál es la mejor manera de convertir ese círculo blanco en azul y almacenar en caché los resultados? ¿Cambia la respuesta si quiero usar esa imagen de origen para crear varios Drawables nuevos (por ejemplo, azul, rojo, verde, naranja, etc.)?

Supongo que querré usar un ColorMatrix de alguna manera, pero no estoy seguro de cómo hacerlo.

Matt McMinn
fuente
2
¿Finalmente conseguiste que esto funcionara de alguna manera? Veo muchas respuestas a continuación, de las cuales probé muchas también, pero nada funciona. Actualmente tengo un cuadrado blanco, que me gustaría colorear de forma diferente en función de las necesidades, para no tener que crear activos estáticos. Por favor sugiera, ya que todavía estoy esperando una solución de trabajo para mi forma simple en color blanco completo.
omkar.ghaisas
@ omkar.ghaisas Construí una biblioteca llamada SillyAndroid que contiene una clase de coloración versátil y hace todo tipo de coloración para dibujos y texto. Puede consultarlo en github.com/milosmns/silly-android . La clase se encuentra en/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
milosmns 01 de

Respuestas:

221

Creo que en realidad solo puedes usar Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). Esto establecería los píxeles blancos en rojo, pero no creo que afecte a los píxeles transparentes.

Ver Drawable # setColorFilter

thom_nic
fuente
9
Esto funcionará bien cuando dibujable es de un solo color, mejor cuando es blanco.
Mitul Nakum
67
Si el color se cambia dinámicamente (por ejemplo, en el Adaptador), el dibujo debe ser mutable. Ejemplo: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)más información: curious-creature.org/2009/05/02/drawable-mutations
sabadow
1
Sí, es especialmente bueno para resaltar (más claro, más oscuro o agregar un tono a una imagen en escala de grises). Uso este truco para alternar botones donde "sin marcar" es escala de grises y "marcado" es un color en negrita de la paleta de colores de mi aplicación. Personalmente, me resulta más fácil que una casilla de verificación personalizada.
thom_nic
2
Esto es exactamente lo que estaba buscando, aunque es increíblemente molesto que no podamos hacer esto en XML ( excepto en 5.0+ ). El tinte ni siquiera está disponible en AppCompat, por lo que nos vemos obligados a llamar setColorFiltercada vez que usamos los íconos en lugar de tener selectores con diferentes tonos de color. Aún así, es una solución mucho mejor que editar pngs directamente y tener activos estáticos adicionales.
Chris Cirefice
21
Multiplicar no funcionará si el icono de origen tiene un color oscuro. Para pintar la forma del icono de origen con el color de destino, use SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Distwo
152

Prueba 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
fuente
106

Sé que esta pregunta se hizo mucho antes de Lollipop, pero me gustaría agregar una buena manera de hacer esto en Android 5. +. Haces un xml dibujable que hace referencia al original y le pones tinte de esta manera:

<?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
fuente
¿Es esta parte de la última biblioteca de soporte también?
S-K '
No. Eso solo ayuda con unos pocos widgets simples.
MinceMan
8
Tint está en soporte-v4 a través de DrawableCompat
Mark Renouf
1
Genial, lo investigaré y actualizaré esto en consecuencia.
MinceMan
Fresco no admite este tipo de
sorteo
63

El nuevo soporte v4 trae el tinte nuevamente a la API 4.

puedes hacerlo así

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}
Pei
fuente
2
A partir de la biblioteca de soporte 22.
rnrneverdies
1
Esta es LA solución preferida, teñir los elementos dibujables ha sido un área gris en las API más antiguas desde que se lanzó lollipop. ¡Esto rompe esa barrera! No sabía sobre esto - gracias @Pei
RicardoSousaDev
2
¡Ten cuidado! Debe mutar su nuevo "#mutate ()" dibujable envuelto para evitar problemas relacionados con el estado. Ver stackoverflow.com/a/44593641/5555218
Ricard
62

Si tiene un dibujo que es un color sólido y desea cambiarlo a un color sólido diferente, puede usar a ColorMatrixColorFilter. La transparencia se conserva.

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
fuente
3
Si desea usar un recurso de color en lugar de una cadena (# ff0000, etc.), puede usar, por ejemplo, int iColor = getResources (). GetColor (R.color.primary)
Ben Clayton
esto funciona pero tengo la casilla de verificación y quiero preservar la marca blanca en el medio. ¿Alguna sugerencia para eso?
Farooq Arshed
3
El código en el comentario de Ben ahora está en desuso. En cambio, puedes usar int iColor = ContextCompat.getColor(context, R.color.primary);.
prohibición de geoingeniería
respuesta brillante !! ¡Gracias a todos!
Kaveesh Kanwal
@Mike Hill Ok, explica por qué pones más de 20 colores. Necesitas insertar más de veinte colores en la matriz porque de lo contrario se bloquea con java.lang.ArrayIndexOutOfBoundsException
AlexPad
50

También uso ImageViewpara iconos (enListView o pantalla de configuración). Pero creo que hay una manera mucho más simple de hacerlo.

Utilizar tint para cambiar la superposición de color en su icono seleccionado.

En xml,

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

funciona bien ya que proviene de AppCompat

sud007
fuente
3
¡Funciona de maravilla! Simple y perfecto Esto debe marcarse como respuesta aceptada.
Naga Mallesh Maddali
2
Aquí hay muchas buenas respuestas, pero para la pregunta de OP esta es la mejor y más simple solución.
Sebastian Breit
para la api 22 y superior
philip oghenerobo balogun
1
@philipoghenerobobalogun vi esto trabajando en la API 19
Jemshit Iskenderov
41

Debe hacer esto para todas las API:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);
hoangtu23
fuente
Esto resolvió el problema de manera aceptable. Pero al filtrar el color, puede suceder (me sucedió a mí) que el color resultante no sea el esperado. El color que debía aclarar. Lo que hice fue: `nuevo LightingColorFilter (Color.parseColor (" # FF000000 "), myFinalColor)`
Yoraco Gonzales
1
Al enfatizar lo que creo que dice el comentarista anterior, esta solución cambia los colores si los 2 parámetros en LightingColorFilter son diferentes, por ejemplo, ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY);cambiarán de negro a gris en el dibujo.
hBrent
1
Parece que esto no funciona cuando se usa alfa para el color de tinte.
ypresto
30

Pude hacer esto con el siguiente código, que se toma de una actividad (el diseño es muy simple, solo contiene un ImageView, y no está publicado aquí).

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
fuente
¿de dónde obtengo el Umbral o el FROM_COLOR?
mikepenz
Esas fueron solo constantes que definí; Acabo de editar la respuesta para incluirlos.
Matt McMinn
gracias;) intenté pero no encaja con el problema que tengo. probé el setColorFilter, y esto funciona pero hay un problema al escalar la imagen .9.png. así que si tienes una idea de por qué, responde mi pregunta. stackoverflow.com/questions/5884481/…
mikepenz
1
Los filtros de color son mucho más fáciles.
afollestad
17

Puede resolverlo utilizando las bibliotecas compatibles con 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
fuente
1
@AmitabhaBiswas ¿Por qué cambias completamente mi respuesta por incorrecta? Parte por parte. 1. getResources (). GetDrawable () está en desuso !! 2. Uso bibliotecas de soporte porque no quiero preocuparme por las versiones de Android Api. 3. No quiero volver a dibujar Drawable ... Si desea mostrar otro enfoque, escriba su propia respuesta.
Ricard
1
@AmitabhaBiswas Además, los recursos extraíbles se comparten entre todos los recursos getDrawable, por lo tanto, mutate()se requiere la llamada para poder cambiar el tono de un elemento extraíble, sin alterar todos los asociados de elementos extraíbles con esa ID de recurso.
Ricard
1
¡Esta es la mejor respuesta! Ajustar dibujable en una vista de imagen no resuelve la pregunta.
Julio
15

En su Actividad, puede teñir sus recursos de imagen PNG con un solo color:

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

Ahora, cuando use R.drawable. *, Debe colorearse con el tinte deseado. Si necesita colores adicionales, entonces debería poder .mutate () el dibujable.

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

Gracias a @sabadow

Hamidreza Sadegh
fuente
setColorFilter en desuso
Mohammad Sommakia
4

Si tiene su conjunto dibujable en ImageView, puede hacerlo con un trazador de líneas 1:

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

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

La API relevante está disponible aquí :

Adrian
fuente
1
Esto muestra cómo usar ColorMatrix, pero no veo cómo usarlo para obtener los resultados que estoy buscando.
Matt McMinn el
3

Esto funciona con todo con fondo:

Vista de texto, botón ...

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

Este fragmento de código funcionó para mí:

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

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

Hay muchas soluciones, pero nadie sugirió que si el archivo xml de recursos de color ya tiene color, podemos elegir directamente desde allí también de la siguiente manera:

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

Breve ejemplo para cambiar el color dibujable de acuerdo con isWorking campo.

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

Mi método para cambiar:

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

Ejemplo de uso:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);
pez muerto
fuente
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> 

Código Java

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

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

Demasiado tarde pero en caso de que alguien lo necesite:

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

Funciona para algunos elementos dibujables simples. Lo utilicé en una forma rect simple de color sólido con esquinas redondeadas y necesitaba cambiar ese color con diferentes diseños.

Prueba esto

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

Es muy muy simple cuando usas una biblioteca para hacer eso por ti. Prueba esta biblioteca

Puedes llamar así:

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