Изменение цвета нарисованного андроида - PullRequest
60 голосов
/ 10 марта 2012

Я бы хотел использовать один и тот же чертеж для представления обоих:

Blue icon и Red icon

как тот же самый объект рисования, и измените цвет рисунка на основе некоторых программных значений, чтобы конечный пользователь мог переназначить интерфейс.

Каков наилучший способ сделать это? Я пробовал (и повторно использовал значки) этот предыдущий S.O. вопрос но я не могу представить изменение как простое изменение оттенка, так как оно также изменяется по насыщенности и значению ..

Лучше ли хранить значок, так как все белое в области, которую я хочу, изменилось? или прозрачный? или какой-то другой сплошной цвет?

Существует ли какой-либо метод, позволяющий вычислить матрицу на основе разницы между цветом red_icon и цветом blue_icon?

Ответы [ 6 ]

107 голосов
/ 25 марта 2012

Итак, после долгих проб и ошибок, прочитав различные статьи и, что самое важное, пройдя через демонстрации API (ColorFilters.java - см. Com.example.android.apis.graphics), я нашел решение.

Я обнаружил, что для сплошных изображений лучше всего использовать цветной фильтр PorterDuff.Mode.SRC_ATOP, поскольку он будет накладываться на цвет поверх исходного изображения, позволяя вам изменить цвет на нужный вам цвет. .

Для изображений, которые являются более сложными, как, например, выше, я обнаружил, что лучше всего закрасить все изображение БЕЛЫМ (FFFFFF), чтобы при выполнении PorterDuff.Mode.MULTIPLY вы получали правильный цвета, и все черное (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, если вы хотите проверить это.

18 голосов
/ 09 декабря 2014

Это действительно легко сделать на 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"/>
12 голосов
/ 04 марта 2015

Ваш ответ очень приятный. Хотя, это решение тоже практично, если вы используете Textview и встраиваемый чертеж:

int colorARGB = R.color.your_color;
Drawable[] textviewDrawables = drawerItem.getCompoundDrawables();
// Left Drawable
textviewDrawables[0].setColorFilter(colorARGB, PorterDuff.Mode.SRC_ATOP);
3 голосов
/ 25 ноября 2016

Если вы хотите применить цветной фильтр к своему изображению в 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

2 голосов
/ 06 декабря 2017

Это то, что я сделал после просмотра документации

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

и назвал ее

yourdrawable.setColorFilter(getDrawableFilter());
2 голосов
/ 21 сентября 2015

Вот что-то лучше, ИМХО, чем принятый ответ. Он получен из этого потока StackOverflow: Понимание использования ColorMatrix и ColorMatrixColorFilter для изменения оттенка рисованного элемента

Пример использования:

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

}
...