2012-03-10 104 views
56

的顏色,我想能夠使用相同的繪製同時代表:修改的機器人繪製

Blue iconRed icon

一樣繪製,並重新色繪製基於一些編程值,以便最終用戶可以重新設定界面的主題。

這樣做的最好方法是什麼?我試過(並重用了this previous S.O. question的圖標),但我無法將其作爲簡單的色調變化來表示,因爲它的飽和度和數值也不相同。

是否最好將圖標存儲爲全白在我想改變的地區?或透明?或其他一些純色?

是否有一些方法可以讓你根據red_icon的顏色和blue_icon的顏色之間的差異找出矩陣?

回答

101

因此,經過大量的試驗和錯誤,閱讀不同的文章,最重要的是,通過API演示(ColorFilters.java - 在com.example.android.apis.graphics中找到),我找到了解決方案。

對於純色圖像,我發現最好使用顏色過濾器PorterDuff.Mode.SRC_ATOP,因爲它會覆蓋源圖像頂部的顏色,允許您將顏色更改爲您正在查找的確切顏色對於。

對於像上面那樣更復雜的圖像,我發現最好的辦法是爲整個圖像着色WHITE(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 Demo代碼在每種顏色過濾器模式之間進行交換,以在不同情況下嘗試它們,並發現它是無價的,所以我想我會在這裏發佈它。

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演示活動直接複製,如果你想測試一下。

+0

如果我的drawable具有純色FFFFFFFF(ARGB)的形狀,並且想用顏色#CCCCCCCC(即,用alpha),我該怎麼做?我嘗試使用SRC_ATOP模式,但似乎alpha給我不同的結果 – dowjones123 2015-05-03 22:30:34

+3

@steve_gregory你是什麼意思的「彩色整個圖像白色(FFFFFF)」? – fahmy 2015-05-09 17:07:53

+5

我並不十分清楚你如何處理你所提到的問題,「把整個圖像着色爲白色(FFFFFF),這樣當你做PorterDuff.Mode.MULTIPLY時,你會得到正確的顏色,並且所有的黑色(000000 )在你的圖像將保持黑色。「你能舉一個例子嗎? – Silmarilos 2015-09-25 18:14:05

18

這對棒棒糖來說非常簡單。做一個XML繪製,並引用你的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"/> 
+0

我有一些項目的圖層列表。其中之一是可繪製的。我如何在一個項目中設置色彩? – Kenji 2016-12-25 09:10:21

+0

您需要間接引入drawable。在這個例子中,src可以說是可繪製的。然後在你的圖層列表中,你可以引用這個可繪製的位圖,而不是直接引用原始繪圖。 。 – MinceMan 2016-12-25 16:00:04

10

您的回答是非常好的。雖然,這種解決方案的做法太多,如果你正在使用一個TextView和一個嵌入繪製:

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

這裏是更好的東西,恕我直言,比接受的答案。正是從這個StackOverflow的線程派生:Understanding the Use of ColorMatrix and ColorMatrixColorFilter to Modify a Drawable's Hue

用法示例:

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 <[email protected]> 
*/ 
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(); 
    } 
    } 

} 
+0

無法在您的帖子中解析方法getHsv()。只是一個頭。 – Silmarilos 2015-09-25 18:11:48

+0

@Silmarilos,謝謝。我更新了答案。 – 2015-09-25 19:12:34

2

在情況下,如果你想彩色濾光片應用於您在ImageView中的圖像可以以更簡單的方式實現。只需在ImageView中使用xml屬性android:tint即可。

例子:

<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

+0

一個很好的解決方案。但是,它需要API 21,而我的目標是API 16,所以尋找另一種方式。 – jk7 2017-05-19 16:34:50

+0

@ jk7爲什麼API 21是必需的? – 2017-05-21 08:27:01

+1

我需要通過諸如setTintList()或setImageTintList()之類的方法爲ImageView或Button設置背景顏色或色調。正是那些需要API 21的方法,所以我使用了DrawableCompat.setTintList()。 – jk7 2017-05-22 17:31:31

1

這是我尋找到文檔

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

後做並把它稱爲

yourdrawable.setColorFilter(getDrawableFilter());