Làm cách nào để thay đổi màu sắc của Drawable trong Android?


271

Tôi đang làm việc trên một ứng dụng Android và tôi có thể rút ra được rằng tôi đang tải lên từ một hình ảnh nguồn. Trên hình ảnh này, tôi muốn chuyển đổi tất cả các pixel trắng sang một màu khác, giả sử là màu xanh lam, và sau đó lưu trữ đối tượng Drawable kết quả để tôi có thể sử dụng nó sau này.

Vì vậy, ví dụ tôi nói rằng tôi có tệp PNG 20x20 có một vòng tròn màu trắng ở giữa và mọi thứ bên ngoài vòng tròn đều trong suốt. Cách tốt nhất để biến vòng tròn trắng đó thành màu xanh và lưu trữ kết quả là gì? Câu trả lời có thay đổi nếu tôi muốn sử dụng hình ảnh nguồn đó để tạo một số Drawables mới (giả sử màu xanh lam, đỏ, xanh lá cây, cam, v.v.) không?

Tôi đoán rằng tôi sẽ muốn sử dụng ColorMatrix theo một cách nào đó, nhưng tôi không chắc bằng cách nào.


2
Cuối cùng bạn đã làm điều này làm việc theo một cách nào đó? Tôi thấy nhiều câu trả lời dưới đây, trong đó tôi cũng đã thử rất nhiều, nhưng không có gì hiệu quả. Tôi hiện đang có một hình vuông màu trắng, mà tôi muốn tô màu khác nhau mỗi lần dựa trên nhu cầu, để tôi không phải tạo tài sản tĩnh. Xin gợi ý, vì tôi vẫn đang chờ đợi một giải pháp làm việc cho hình dạng đơn giản của tôi với màu trắng đầy đủ.
omkar.ghaisas

@ omkar.ghaisas Tôi đã xây dựng một thư viện có tên SillyAndroid chứa một lớp Tô màu linh hoạt và thực hiện tất cả các loại màu cho các hình vẽ và văn bản. Bạn có thể kiểm tra nó tại github.com/milosmns/silly-android . Lớp học được đặt tại/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
milosmns

Câu trả lời:


221

Tôi nghĩ rằng bạn thực sự có thể chỉ cần sử dụng Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ) . Điều này sẽ đặt các pixel trắng thành đỏ nhưng tôi không nghĩ nó sẽ ảnh hưởng đến các pixel trong suốt.

Xem Drawable # setColorFilter


9
Điều này sẽ hoạt động tốt khi drawable là màu đơn, tốt hơn khi màu trắng của nó.
Mitul Nakum

67
Nếu màu sắc bị thay đổi dinammicaly (ví dụ trong Adaptor), drawable phải có thể thay đổi. Ví dụ: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)thêm thông tin: tò mò
creature.org / 2009/05/02 /drawable

1
Vâng, nó đặc biệt tốt để làm nổi bật (sáng hơn, tối hơn hoặc thêm màu sắc cho hình ảnh màu xám.) Tôi sử dụng mẹo này để chuyển các nút trong đó "không được chọn" là màu xám & "được kiểm tra" là màu đậm từ bảng màu của ứng dụng. Cá nhân tôi thấy nó dễ dàng hơn một hộp kiểm tùy chỉnh.
thom_nic

2
Đây chính xác là những gì tôi đang tìm kiếm, mặc dù thật khó chịu khi chúng tôi không thể làm điều này bằng XML ( ngoại trừ ngày 5.0 trở lên ). Việc tô màu thậm chí không khả dụng trong AppCompat, vì vậy chúng tôi bị mắc kẹt khi phải gọi setColorFiltermỗi khi chúng tôi sử dụng các biểu tượng thay vì có các bộ chọn với các sắc độ màu khác nhau. Tuy nhiên, đó là một giải pháp tốt hơn nhiều so với việc chỉnh sửa pngs trực tiếp và có thêm tài sản tĩnh.
Chris Cirefice

21
Nhân sẽ không hoạt động nếu biểu tượng nguồn của bạn có màu tối. Để vẽ hình dạng biểu tượng nguồn với màu đích, hãy sử dụng SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Distwo

152

Hãy thử mã này:

ImageView lineColorCode = (ImageView)convertView.findViewById(R.id.line_color_code);
int color = Color.parseColor("#AE6118"); //The color u want             
lineColorCode.setColorFilter(color);

106

Tôi biết câu hỏi này đã được hỏi trước Lollipop nhưng tôi muốn thêm một cách hay để làm điều này trên Android 5. +. Bạn tạo một xml có thể vẽ tham chiếu đến bản gốc và đặt màu trên đó như sau:

<?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"/>

đây cũng là một phần của thư viện hỗ trợ mới nhất phải không?
S-K '

Không. Điều đó chỉ giúp với một vài widget đơn giản.
MinceMan

8
Tint nằm trong support-v4 thông qua DrawableCompat
Mark Renouf

1
Thật tuyệt, tôi sẽ xem xét điều đó và cập nhật điều này cho phù hợp.
MinceMan

Fresco không hỗ trợ loại có thể rút được này
jackqack

62

Hỗ trợ mới v4 mang lại màu sắc cho api 4.

bạn có thể làm như thế này

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}

2
Bắt đầu từ thư viện hỗ trợ 22.
rnrneverdies

1
Đây là giải pháp ưa thích, các màu vẽ có thể là một vùng màu xám trong các API cũ hơn kể từ khi kẹo mút được phát hành. Cái phanh này mà rào cản! Tôi không biết về việc này - nhờ @Pei
RicardoSousaDev

2
Hãy cẩn thận! Bạn nên thay đổi "#mutate ()" được gói mới của bạn để tránh các vấn đề liên quan đến trạng thái. Xem stackoverflow.com/a/44593641/5555218
Ricard

62

Nếu bạn có một màu có thể vẽ đó là một màu đơn sắc và bạn muốn thay đổi nó thành một màu đơn sắc differnet, bạn có thể sử dụng một ColorMatrixColorFilter. Minh bạch được bảo tồn.

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

3
Nếu bạn muốn sử dụng tài nguyên màu thay vì chuỗi (# ff0000, v.v.), bạn có thể sử dụng ví dụ: int iColor = getResource (). GetColor (R.color.primary)
Ben Clayton

cái này hoạt động nhưng tôi có hộp kiểm và tôi muốn giữ dấu trắng ở giữa. Bất kỳ đề nghị cho điều đó?
Farooq Arshed

3
Các mã trong nhận xét của Ben hiện không được chấp nhận. Thay vào đó, bạn có thể sử dụng int iColor = ContextCompat.getColor(context, R.color.primary);.
ban-geengineering

câu trả lời tuyệt vời !! Cảm ơn tất cả!
Kaveesh Kanwal

@Mike Hill Ok, giải thích lý do tại sao bạn đặt hơn 20 màu. Bạn cần chèn lớn hơn hai mươi màu trong mảng vì nó bị lỗi với java.lang.ArrayIndexOutOfBoundException
AlexPad

50

Tôi cũng sử dụng ImageViewcho các biểu tượng (trong ListViewhoặc màn hình cài đặt). Nhưng tôi nghĩ có nhiều cách đơn giản hơn để làm điều đó.

Sử dụng tintđể thay đổi lớp phủ màu trên biểu tượng đã chọn của bạn.

Trong xml,

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

hoạt động tốt vì nó đến từ AppCompat


3
Hoạt động như một lá bùa! Đơn giản và hoàn hảo. Điều này cần được đánh dấu là câu trả lời được chấp nhận.
Naga Mallesh Maddali

2
Có rất nhiều câu trả lời hay ở đây, nhưng đối với câu hỏi của OP thì đây là giải pháp tốt nhất và đơn giản nhất.
Sebastian Breit

cho api 22 trở lên
philip oghenerobo balogun

1
@philipoghenerobobalogun tôi đã thấy điều này hoạt động trên api 19
Jemshit Iskenderov

41

Bạn nên làm điều này cho tất cả các API:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);

Điều này giải quyết vấn đề một cách chấp nhận được. Nhưng khi lọc màu, có thể xảy ra (nó xảy ra với tôi) rằng màu kết quả không như mong đợi. Màu đó là để làm sáng. Những gì tôi đã làm là: `Ánh sáng mớiColorFilter (Color.parseColor (" # FF000000 "), myFinalColor)`
Yoraco Gonzales

1
Nhấn mạnh những gì tôi nghĩ người bình luận trước đang nói, giải pháp này thay đổi màu sắc nếu 2 tham số trong LightingColorFilter khác nhau, ví dụ, ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY);sẽ chuyển màu đen thành màu xám trong hình vẽ.
hBrent

1
Điều này dường như không hoạt động khi alpha được sử dụng cho màu sắc.
ypresto

30

Tôi đã có thể làm điều này với đoạn mã sau, được lấy từ một hoạt động (bố cục rất đơn giản, chỉ chứa ImageView và không được đăng ở đây).

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

Tôi lấy Ngưỡng hoặc TỪ_COLOR từ đâu?
mikepenz

Đó chỉ là hằng số mà tôi xác định; Tôi chỉ chỉnh sửa câu trả lời để bao gồm chúng.
Matt McMinn

cảm ơn bạn;) đã cố gắng nhưng nó không phù hợp với vấn đề tôi có. đã thử setColorFilter và điều này hoạt động nhưng có vấn đề với việc thu nhỏ hình ảnh .9.png. Vì vậy, nếu bạn có một ý tưởng tại sao, xin vui lòng trả lời câu hỏi của tôi. stackoverflow.com/questions/5884481/ từ
mikepenz

1
Bộ lọc màu dễ dàng hơn nhiều.
afollestad

17

Bạn có thể giải quyết nó bằng thư viện compat hỗ trợ 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))

1
@AmitabhaBiswas Tại sao bạn hoàn toàn thay đổi để trả lời sai của tôi? Từng phần. 1. getResource (). GetDrawable () không được dùng nữa !! 2. Tôi sử dụng các thư viện hỗ trợ vì tôi không muốn quan tâm đến các phiên bản Andorid Api. 3. Tôi không muốn vẽ lại Drawable .... Nếu bạn muốn hiển thị một cách tiếp cận khác, hãy viết câu trả lời của riêng bạn.
Ricard

1
@AmitabhaBiswas Bên cạnh đó, các drawable được chia sẻ giữa tất cả getDrawable từ các tài nguyên, do đó mutate()cuộc gọi được yêu cầu để có thể thay đổi sắc độ của drawable, mà không thay đổi tất cả các drawable liên kết với ID tài nguyên đó.
Ricard

1
Đây là câu trả lời tốt nhất! Gói có thể vẽ trong chế độ xem hình ảnh không giải quyết được câu hỏi.
Julius

15

Trong Hoạt động của bạn, bạn có thể tô màu tài nguyên hình ảnh PNG của mình bằng một màu duy nhất:

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

Bây giờ khi bạn sử dụng R.drawable. * Nó sẽ được tô màu với tông màu mong muốn. Nếu bạn cần thêm màu thì bạn sẽ có thể .mutate () có thể vẽ được.



4

Nếu bạn có bộ có thể vẽ của mình thành ImageView, bạn có thể thực hiện với 1 lớp lót:

yourImageView.setColorFilter(context.getResources().getColor(R.color.YOUR_COLOR_HERE);

3

Kiểm tra mã mẫu này " 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();
        }
    }
}

API có liên quan có sẵn ở đây :


1
Điều này không chỉ ra cách sử dụng ColorMatrix, nhưng tôi không thấy cách sử dụng nó để có được kết quả mà tôi đang tìm kiếm.
Matt McMinn

3

Điều này hoạt động với tất cả mọi thứ với nền:

Xem văn bản, nút ...

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

3

Đoạn mã này đã làm việc cho tôi:

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

imgView.getDrawable().setColorFilter(porterDuffColorFilter);
imgView.setBackgroundColor(Color.TRANSPARENT)

2

Có rất nhiều giải pháp nhưng không ai đề xuất nếu tệp xml tài nguyên màu đã có màu thì chúng ta có thể chọn trực tiếp từ đó như sau:

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

1

Ví dụ ngắn để thay đổi màu vẽ theo isWorking trường.

Hình dạng của tôi 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>

Phương pháp của tôi để thay đổi:

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

Ví dụ về cách sử dụng:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);

0
Int color = Color.GRAY; 
// or int color = Color.argb(123,255,0,5);
// or int color = 0xaaff000;

trong XML /res/values/color.xml

<?xml version="1.0" encoding="utf-8">
<resources>
    <color name="colorRed">#ff0000</color>
</resoures> 

Mã Java

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

GradientDrawable drawableBg = yourView.getBackground().mutate();
drawableBg.setColor(color);

0

Quá muộn nhưng trong trường hợp ai đó cần nó:

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

0

Nó hoạt động cho một số drawables đơn giản. Tôi đã sử dụng nó trên một hình dạng trực tràng màu đơn giản với các góc tròn và cần thiết để thay đổi màu sắc đó với các bố cục khác nhau.

Thử cái này

android:backgroundTint="#101010"

-1

Rất đơn giản khi bạn sử dụng một thư viện để làm điều đó cho bạn. Thử cái này thư viện

Bạn có thể gọi như thế này:

Icon.on(holderView).color(R.color.your_color).icon(R.mipmap.your_icon).put();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.