Изменение цвета android drawable
Я хотел бы иметь возможность использовать один и тот же drawable для представления обоих:
и
как тот же drawable, и перекрасить drawable на основе некоторых программных значений, так что конечный пользователь может повторно тему интерфейса.
каков наилучший способ сделать это? Я пробовал (и повторно использовал значки)этот предыдущий S. O. вопрос но я не могу представить это изменение как простое изменение оттенка, так как оно также варьируется в насыщенности и значение..
лучше ли хранить значок как все белое в области, которую я хочу изменить? или прозрачным? или другой сплошной цвет?
есть ли какой-то метод, который позволяет вычислить матрицу на основе разницы между цветом red_icon и цветом blue_icon?
6 ответов:
Итак, после множества проб и ошибок, читая разные статьи, а самое главное, проходя через API Demos (ColorFilters.java -- найдено в com.образец.андроид.Апис.графика) я нашел решение.
для твердых изображений, я нашел, что лучше всего использовать цветной фильтр PorterDuff.Режим.SRC_ATOP, потому что он будет накладывать цвет поверх исходного изображения, что позволяет изменить цвет на точный цвет, который вы ищете.
для изображений, которые больше сложный, как и выше, я нашел, что лучше всего сделать, это покрасить все изображение в белый цвет (FFFFFF), чтобы, когда вы делаете PorterDuff.Режим.Умножьте, вы получите правильные цвета, и все черное (000000) в вашем изображении останется черным.
в colorfilters.java показывает вам, как это делается, если ваш рисунок на холсте, но если все, что вам нужно, это покрасить рисование, то это будет работать:
COLOR2 = Color.parseColor("#FF"+getColor()); Mode mMode = Mode.SRC_ATOP; Drawable d = mCtx.getResources().getDrawable(R.drawable.image); d.setColorFilter(COLOR2,mMode)
Я создал демо-активность, используя некоторые из демо-кода API, чтобы переключайтесь между каждым режимом цветового фильтра, чтобы попробовать их для разных ситуаций и нашли его неоценимым, поэтому я подумал, что опубликую его здесь.
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.getIntrinsicHeight(); int y = center - (h >> 1); curr.setBounds(x, y, x + curr.getIntrinsicWidth(), 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.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); 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; } } }
две другие зависимости, GraphicsActivity.java и PictureLayout.java, можно скопировать непосредственно из API Demos activity, если вы хотите проверить его.
Это очень легко сделать на Lollipop. Сделайте xml drawable и ссылайтесь на свой png и установите оттенок следующим образом:
<?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"/>
ваш ответ очень хороший. Хотя, это решение тоже практика, если вы используете Textview и embed drawable:
int colorARGB = R.color.your_color; Drawable[] textviewDrawables = drawerItem.getCompoundDrawables(); // Left Drawable textviewDrawables[0].setColorFilter(colorARGB, PorterDuff.Mode.SRC_ATOP);
в случае, если вы хотите применить цветовой фильтр к изображению в ImageView вы можете реализовать его еще проще. Просто используйте атрибут
android:tint
в ImageView в xml.пример:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/your_drawable" android:tint="@color/your_color" />
проверено на Android 4.1.2 и 6.0.1
вот что-то лучше, ИМХО, чем принятый ответ. Он является производным от этого потока StackOverflow:понимание использования ColorMatrix и ColorMatrixColorFilter для изменения оттенка Drawable
пример использования:
ImageView imageView = ...; Drawable drawable = imageView.getDrawable(); ColorFilter colorFilter = ColorFilterGenerator.from(drawable).to(Color.RED); imageView.setColorFilter(colorFilter);
скопируйте класс в свой проект:
import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.PictureDrawable; import android.widget.ImageView; /** * Creates a {@link ColorMatrixColorFilter} to adjust the hue, saturation, brightness, or * contrast of an {@link Bitmap}, {@link Drawable}, or {@link ImageView}. * <p/> * Example usage: * <br/> * {@code imageView.setColorFilter(ColorFilterGenerator.from(Color.BLUE).to(Color.RED));} * * @author Jared Rummler <jared.rummler@gmail.com> */ 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(ColorMatrix 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 ColorMatrix(mat)); } public static void adjustBrightness(ColorMatrix 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 ColorMatrix(mat)); } public static void adjustContrast(ColorMatrix 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 ColorMatrix(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.getIntrinsicWidth(), pictureDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.drawPicture(pictureDrawable.getPicture()); return bitmap; } int width = drawable.getIntrinsicWidth(); width = width > 0 ? width : 1; int height = drawable.getIntrinsicHeight(); 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(); } } }