Modification de la couleur d’un dessin Android

Je voudrais pouvoir utiliser le même tirable pour représenter les deux:

Icône bleue et Icône rouge

comme le même tirable, et re-colorier le dessinable en fonction de certaines valeurs de programmation, de sorte que l’utilisateur final pourrait re-thème l’interface.

Quelle est la meilleure façon de procéder? J’ai essayé (et réutilisé les icons de) cette précédente question SO mais je ne peux pas représenter le changement comme un simple changement de teinte, car sa saturation et sa valeur varient également.

Est-il préférable de stocker l’icône comme tout blanc dans la zone que je souhaite modifier? ou transparent? ou une autre couleur unie?

Existe-t-il une méthode permettant de déterminer la masortingce en fonction de la différence entre Couleur de red_icon et Couleur de blue_icon?

Donc, après beaucoup d’essais et d’erreurs, en lisant différents articles, et surtout, en parcourant les démos d’API (ColorFilters.java – trouvées dans com.example.android.apis.graphics), j’ai trouvé la solution.

Pour les images solides, j’ai trouvé qu’il est préférable d’utiliser le filtre de couleur PorterDuff.Mode.SRC_ATOP car il superpose la couleur au-dessus de l’image source, vous permettant de changer la couleur à la couleur exacte que vous recherchez.

Pour les images plus complexes, comme celle ci-dessus, j’ai trouvé que la meilleure chose à faire était de colorer l’image entière WHITE (FFFFFF) de sorte que lorsque vous faites PorterDuff.Mode.MULTIPLY, vous vous retrouviez avec les bonnes couleurs, et tout le noir (000000) de votre image restra noir.

Le colorfilters.java vous montre comment cela se passe si vous dessinez sur un canevas, mais si tout ce dont vous avez besoin est de colorier un canevas, cela fonctionnera:

COLOR2 = Color.parseColor("#FF"+getColor()); Mode mMode = Mode.SRC_ATOP; Drawable d = mCtx.getResources().getDrawable(R.drawable.image); d.setColorFilter(COLOR2,mMode) 

J’ai créé une activité de démonstration en utilisant une partie du code de démo de l’API pour basculer entre chaque mode de filtre de couleur pour les essayer dans différentes situations et je l’ai trouvé précieux, alors j’ai pensé le poster ici.

 public class ColorFilters extends GraphicsActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new SampleView(this)); } private static class SampleView extends View { private Activity mActivity; private Drawable mDrawable; private Drawable[] mDrawables; private Paint mPaint; private Paint mPaint2; private float mPaintTextOffset; private int[] mColors; private PorterDuff.Mode[] mModes; private int mModeIndex; private Typeface futura_bold; private AssetManager assets; private static void addToTheRight(Drawable curr, Drawable prev) { Rect r = prev.getBounds(); int x = r.right + 12; int center = (r.top + r.bottom) >> 1; int h = curr.getInsortingnsicHeight(); int y = center - (h >> 1); curr.setBounds(x, y, x + curr.getInsortingnsicWidth(), y + h); } public SampleView(Activity activity) { super(activity); mActivity = activity; Context context = activity; setFocusable(true); /**1. GET DRAWABLE, SET BOUNDS */ assets = context.getAssets(); mDrawable = context.getResources().getDrawable(R.drawable.roundrect_gray_button_bg_nine); mDrawable.setBounds(0, 0, mDrawable.getInsortingnsicWidth(), mDrawable.getInsortingnsicHeight()); mDrawable.setDither(true); int[] resIDs = new int[] { R.drawable.roundrect_gray_button_bg, R.drawable.order_button_white, R.drawable.yellowbar }; mDrawables = new Drawable[resIDs.length]; Drawable prev = mDrawable; for (int i = 0; i < resIDs.length; i++) { mDrawables[i] = context.getResources().getDrawable(resIDs[i]); mDrawables[i].setDither(true); addToTheRight(mDrawables[i], prev); prev = mDrawables[i]; } /**2. SET Paint for writing text on buttons */ mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setTextSize(16); mPaint.setTextAlign(Paint.Align.CENTER); mPaint2 = new Paint(mPaint); /** Calculating size based on font */ futura_bold = Typeface.createFromAsset(assets, "fonts/futurastd-bold.otf"); //Determine size and offset to write text in label based on font size. mPaint.setTypeface(futura_bold); Paint.FontMetrics fm = mPaint.getFontMetrics(); mPaintTextOffset = (fm.descent + fm.ascent) * 0.5f; mColors = new int[] { 0, 0xFFA60017,//WE USE THESE 0xFFC6D405, 0xFF4B5B98, 0xFF656565, 0xFF8888FF, 0xFF4444FF, }; mModes = new PorterDuff.Mode[] { PorterDuff.Mode.DARKEN, PorterDuff.Mode.DST, PorterDuff.Mode.DST_ATOP, PorterDuff.Mode.DST_IN, PorterDuff.Mode.DST_OUT, PorterDuff.Mode.DST_OVER, PorterDuff.Mode.LIGHTEN, PorterDuff.Mode.MULTIPLY, PorterDuff.Mode.SCREEN, PorterDuff.Mode.SRC, PorterDuff.Mode.SRC_ATOP, PorterDuff.Mode.SRC_IN, PorterDuff.Mode.SRC_OUT, PorterDuff.Mode.SRC_OVER, PorterDuff.Mode.XOR }; mModeIndex = 0; updateTitle(); } private void swapPaintColors() { if (mPaint.getColor() == 0xFF000000) { mPaint.setColor(0xFFFFFFFF); mPaint2.setColor(0xFF000000); } else { mPaint.setColor(0xFF000000); mPaint2.setColor(0xFFFFFFFF); } mPaint2.setAlpha(0); } private void updateTitle() { mActivity.setTitle(mModes[mModeIndex].toString()); } private void drawSample(Canvas canvas, ColorFilter filter) { /** Create a rect around bounds, ensure size offset */ Rect r = mDrawable.getBounds(); float x = (r.left + r.right) * 0.5f; float y = (r.top + r.bottom) * 0.5f - mPaintTextOffset; /**Set color filter to selected color * create canvas (filled with this color) * Write text using paint (new color) */ mDrawable.setColorFilter(filter); mDrawable.draw(canvas); /** If the text doesn't fit in the button, make the text size smaller until it does*/ final float size = mPaint.measureText("Label"); if((int) size > (r.right-r.left)) { float ts = mPaint.getTextSize(); Log.w("DEBUG","Text size was"+ts); mPaint.setTextSize(ts-2); } canvas.drawText("Sausage Burrito", x, y, mPaint); /** Write the text and draw it onto the drawable*/ for (Drawable dr : mDrawables) { dr.setColorFilter(filter); dr.draw(canvas); } } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(0xFFCCCCCC); canvas.translate(8, 12); for (int color : mColors) { ColorFilter filter; if (color == 0) { filter = null; } else { filter = new PorterDuffColorFilter(color, mModes[mModeIndex]); } drawSample(canvas, filter); canvas.translate(0, 55); } } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: // update mode every other time we change paint colors if (mPaint.getColor() == 0xFFFFFFFF) { mModeIndex = (mModeIndex + 1) % mModes.length; updateTitle(); } swapPaintColors(); invalidate(); break; } return true; } } } 

Les deux autres dépendances, GraphicsActivity.java et PictureLayout.java, peuvent être copiées directement à partir de l’activité de démonstration de l’API si vous souhaitez les tester.

C’est vraiment facile à faire sur Lollipop. Faites un fichier XML dessinable et référencez votre png et définissez la teinte comme ceci:

   

Votre réponse est très gentille. Bien que, cette solution est pratique aussi si vous utilisez un Textview et un dessin incorporable:

 int colorARGB = R.color.your_color; Drawable[] textviewDrawables = drawerItem.getCompoundDrawables(); // Left Drawable textviewDrawables[0].setColorFilter(colorARGB, PorterDuff.Mode.SRC_ATOP); 

Si vous souhaitez appliquer un filtre de couleur à votre image dans ImageView, vous pouvez l’implémenter plus facilement. Il suffit d’utiliser l’atsortingbut android:tint dans ImageView en XML.

Exemple:

  

Testé sur Android 4.1.2 et 6.0.1

Voici quelque chose de mieux, à mon humble avis, que la réponse acceptée. Il est dérivé de ce thread StackOverflow: Comprendre l’utilisation de ColorMasortingx et ColorMasortingxColorFilter pour modifier une teinte pouvant être dessinée

Exemple d’utilisation:

 ImageView imageView = ...; Drawable drawable = imageView.getDrawable(); ColorFilter colorFilter = ColorFilterGenerator.from(drawable).to(Color.RED); imageView.setColorFilter(colorFilter); 

Copiez la classe dans votre projet:

 import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.ColorMasortingx; import android.graphics.ColorMasortingxColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.PictureDrawable; import android.widget.ImageView; /** * Creates a {@link ColorMasortingxColorFilter} to adjust the hue, saturation, brightness, or * contrast of an {@link Bitmap}, {@link Drawable}, or {@link ImageView}. * 

* Example usage: *
* {@code imageView.setColorFilter(ColorFilterGenerator.from(Color.BLUE).to(Color.RED));} * * @author Jared Rummler */ public class ColorFilterGenerator { // Based off answer from StackOverflow // See: https://stackoverflow.com/a/15119089/1048340 private ColorFilterGenerator() { throw new AssertionError(); } public static From from(Drawable drawable) { return new From(drawableToBitmap(drawable)); } public static From from(Bitmap bitmap) { return new From(bitmap); } public static From from(int color) { return new From(color); } // -------------------------------------------------------------------------------------------- private static final double DELTA_INDEX[] = { 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24, 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98, 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8, 10.0 }; public static void adjustHue(ColorMasortingx cm, float value) { value = cleanValue(value, 180f) / 180f * (float) Math.PI; if (value == 0) { return; } float cosVal = (float) Math.cos(value); float sinVal = (float) Math.sin(value); float lumR = 0.213f; float lumG = 0.715f; float lumB = 0.072f; float[] mat = new float[]{ lumR + cosVal * (1 - lumR) + sinVal * (-lumR), lumG + cosVal * (-lumG) + sinVal * (-lumG), lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0, lumR + cosVal * (-lumR) + sinVal * (0.143f), lumG + cosVal * (1 - lumG) + sinVal * (0.140f), lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0, lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), lumG + cosVal * (-lumG) + sinVal * (lumG), lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, 1f }; cm.postConcat(new ColorMasortingx(mat)); } public static void adjustBrightness(ColorMasortingx cm, float value) { value = cleanValue(value, 100); if (value == 0) { return; } float[] mat = new float[]{ 1, 0, 0, 0, value, 0, 1, 0, 0, value, 0, 0, 1, 0, value, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 }; cm.postConcat(new ColorMasortingx(mat)); } public static void adjustContrast(ColorMasortingx cm, int value) { value = (int) cleanValue(value, 100); if (value == 0) { return; } float x; if (value < 0) { x = 127 + value / 100 * 127; } else { x = value % 1; if (x == 0) { x = (float) DELTA_INDEX[value]; } else { x = (float) DELTA_INDEX[(value << 0)] * (1 - x) + (float) DELTA_INDEX[(value << 0) + 1] * x; } x = x * 127 + 127; } float[] mat = new float[]{ x / 127, 0, 0, 0, 0.5f * (127 - x), 0, x / 127, 0, 0, 0.5f * (127 - x), 0, 0, x / 127, 0, 0.5f * (127 - x), 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 }; cm.postConcat(new ColorMatrix(mat)); } public static void adjustSaturation(ColorMatrix cm, float value) { value = cleanValue(value, 100); if (value == 0) { return; } float x = 1 + ((value > 0) ? 3 * value / 100 : value / 100); float lumR = 0.3086f; float lumG = 0.6094f; float lumB = 0.0820f; float[] mat = new float[]{ lumR * (1 - x) + x, lumG * (1 - x), lumB * (1 - x), 0, 0, lumR * (1 - x), lumG * (1 - x) + x, lumB * (1 - x), 0, 0, lumR * (1 - x), lumG * (1 - x), lumB * (1 - x) + x, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 }; cm.postConcat(new ColorMasortingx(mat)); } // -------------------------------------------------------------------------------------------- private static float cleanValue(float p_val, float p_limit) { return Math.min(p_limit, Math.max(-p_limit, p_val)); } private static float[] getHsv(int color) { float[] hsv = new float[3]; Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color), hsv); return hsv; } /** * Converts a {@link Drawable} to a {@link Bitmap} * * @param drawable * The {@link Drawable} to convert * @return The converted {@link Bitmap}. */ private static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } else if (drawable instanceof PictureDrawable) { PictureDrawable pictureDrawable = (PictureDrawable) drawable; Bitmap bitmap = Bitmap.createBitmap(pictureDrawable.getInsortingnsicWidth(), pictureDrawable.getInsortingnsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.drawPicture(pictureDrawable.getPicture()); return bitmap; } int width = drawable.getInsortingnsicWidth(); width = width > 0 ? width : 1; int height = drawable.getInsortingnsicHeight(); height = height > 0 ? height : 1; Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } /** * Calculate the average red, green, blue color values of a bitmap * * @param bitmap * a {@link Bitmap} * @return */ private static int[] getAverageColorRGB(Bitmap bitmap) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); int size = width * height; int[] pixels = new int[size]; int r, g, b; r = g = b = 0; bitmap.getPixels(pixels, 0, width, 0, 0, width, height); for (int i = 0; i < size; i++) { int pixelColor = pixels[i]; if (pixelColor == Color.TRANSPARENT) { size--; continue; } r += Color.red(pixelColor); g += Color.green(pixelColor); b += Color.blue(pixelColor); } r /= size; g /= size; b /= size; return new int[]{ r, g, b }; } /** * Calculate the average color value of a bitmap * * @param bitmap * a {@link Bitmap} * @return */ private static int getAverageColor(Bitmap bitmap) { int[] rgb = getAverageColorRGB(bitmap); return Color.argb(255, rgb[0], rgb[1], rgb[2]); } // Builder // -------------------------------------------------------------------------------------------- public static final class Builder { int hue; int contrast; int brightness; int saturation; public Builder setHue(int hue) { this.hue = hue; return this; } public Builder setContrast(int contrast) { this.contrast = contrast; return this; } public Builder setBrightness(int brightness) { this.brightness = brightness; return this; } public Builder setSaturation(int saturation) { this.saturation = saturation; return this; } public ColorFilter build() { ColorMatrix cm = new ColorMatrix(); adjustHue(cm, hue); adjustContrast(cm, contrast); adjustBrightness(cm, brightness); adjustSaturation(cm, saturation); return new ColorMatrixColorFilter(cm); } } public static final class From { final int oldColor; private From(Bitmap bitmap) { oldColor = getAverageColor(bitmap); } private From(int oldColor) { this.oldColor = oldColor; } public ColorFilter to(int newColor) { float[] hsv1 = getHsv(oldColor); float[] hsv2 = getHsv(newColor); int hue = (int) (hsv2[0] - hsv1[0]); int saturation = (int) (hsv2[1] - hsv1[1]); int brightness = (int) (hsv2[2] - hsv1[2]); return new ColorFilterGenerator.Builder() .setHue(hue) .setSaturation(saturation) .setBrightness(brightness) .build(); } } }

C’est ce que j’ai fait après avoir examiné la documentation

 public PorterDuffColorFilter getDrawableFilter(){ return new PorterDuffColorFilter(ContextCompat.getColor(this, R.color.color_black), PorterDuff.Mode.SRC_ATOP); } 

et l’a appelé

 yourdrawable.setColorFilter(getDrawableFilter());