Bagaimana cara mengubah warna Drawable di Android?

271

Saya sedang mengerjakan aplikasi android, dan saya memiliki gambar yang dapat saya muat dari gambar sumber. Pada gambar ini, saya ingin mengonversi semua piksel putih ke warna yang berbeda, katakan biru, dan kemudian cache objek Drawable yang dihasilkan sehingga saya bisa menggunakannya nanti.

Jadi misalnya katakan saya memiliki file PNG 20x20 yang memiliki lingkaran putih di tengah, dan segala sesuatu di luar lingkaran transparan. Apa cara terbaik untuk mengubah lingkaran putih itu menjadi biru dan menyimpan hasilnya? Apakah jawabannya berubah jika saya ingin menggunakan gambar sumber itu untuk membuat beberapa Drawables baru (misalnya biru, merah, hijau, oranye, dll)?

Saya menduga bahwa saya ingin menggunakan ColorMatrix dalam beberapa cara, tapi saya tidak yakin bagaimana caranya.

Matt McMinn
sumber
2
Apakah Anda akhirnya berhasil dalam beberapa cara? Saya memang melihat banyak jawaban di bawah, dari mana saya mencoba banyak juga, tetapi tidak ada yang berhasil. Saat ini saya memiliki kotak putih, yang ingin saya warnai berbeda setiap saat berdasarkan kebutuhan, sehingga saya tidak perlu membuat aset statis. Tolong sarankan, karena saya masih menunggu solusi kerja untuk bentuk sederhana saya dalam warna putih penuh.
omkar.ghaisas
@ omkar.ghaisas Saya membangun perpustakaan bernama SillyAndroid yang berisi kelas Pewarnaan serbaguna dan mengerjakan segala macam pewarnaan untuk gambar dan teks. Anda dapat memeriksanya di github.com/milosmns/silly-android . Kelas ini terletak di/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
milosmns

Jawaban:

221

Saya pikir Anda benar-benar bisa hanya menggunakan Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). Ini akan mengatur piksel putih menjadi merah tetapi saya tidak berpikir itu akan mempengaruhi piksel transparan.

Lihat Drawable # setColorFilter

thom_nic
sumber
9
Ini akan bekerja dengan baik bila gambarnya berwarna tunggal, lebih baik bila warnanya putih.
Mitul Nakum
67
Jika warnanya berubah dinammicaly (misalnya dalam Adapter), drawable harus dapat diubah. Contoh: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)info lebih lanjut: curious-creature.org/2009/05/02/drawable-mutations
sabadow
1
Yup, ini sangat baik untuk menyoroti (lebih terang, lebih gelap, atau menambahkan rona ke gambar skala abu-abu.) Saya menggunakan trik ini untuk beralih tombol di mana "tidak dicentang" adalah skala abu-abu & "dicentang" adalah warna yang berani dari palet warna aplikasi saya. Secara pribadi saya merasa lebih mudah daripada kotak centang khusus.
thom_nic
2
Ini persis apa yang saya cari, meskipun sangat menjengkelkan bahwa kita tidak dapat melakukan ini dalam XML ( kecuali untuk pada 5.0+ ). Pewarnaan bahkan tidak tersedia di AppCompat, jadi kami tidak dapat menelepon setColorFiltersetiap kali kami menggunakan ikon alih-alih memiliki pemilih dengan warna berbeda. Namun, ini adalah solusi yang jauh lebih baik daripada mengedit png secara langsung dan memiliki aset statis ekstra.
Chris Cirefice
21
Multiply tidak akan berfungsi jika ikon sumber Anda memiliki warna gelap. Untuk melukis bentuk ikon sumber dengan warna tujuan gunakan SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Distwo
152

Cobalah kode ini:

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

Saya tahu pertanyaan ini diajukan jauh sebelum Lollipop tetapi saya ingin menambahkan cara yang bagus untuk melakukan ini di Android 5. +. Anda membuat xml drawable yang mereferensikan yang asli dan menetapkan warna di atasnya seperti:

<?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"/>
Tuan Putra
sumber
Apakah ini bagian dari perpustakaan dukungan terbaru juga?
S-K '
Tidak. Itu hanya membantu dengan beberapa widget sederhana.
MinceMan
8
Tint ada di support-v4 melalui DrawableCompat
Mark Renouf
1
Keren saya akan melihat itu dan memperbarui ini sesuai.
MinceMan
Fresco tidak mendukung jenis
drawable ini
62

Dukungan v4 baru membawa warna kembali ke api 4.

Anda bisa melakukannya seperti ini

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}
Pei
sumber
2
Mulai dari perpustakaan dukungan 22.
rnrneverdies
1
Ini adalah solusi yang disukai, mewarnai drawable telah menjadi area abu-abu di API lama sejak lollipop dirilis. Ini mengerem penghalang itu! Saya tidak tahu tentang yang ini - terima kasih @Pei
RicardoSousaDev
2
Hati-hati! Anda harus memutasikan "#mutate ()" Anda yang baru dibungkus untuk menghindari masalah terkait negara. Lihat stackoverflow.com/a/44593641/5555218
Ricard
62

Jika Anda memiliki drawable yang berwarna solid dan Anda ingin mengubahnya menjadi warna solid differnet, Anda dapat menggunakan a ColorMatrixColorFilter. Transparansi dipertahankan.

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);
Mike Hill
sumber
3
Jika Anda ingin menggunakan sumber daya warna daripada string (# ff0000 dll), Anda dapat menggunakan misalnya int iColor = getResources (). GetColor (R.color.primary)
Ben Clayton
ini berfungsi tetapi saya memiliki kotak centang dan saya ingin mempertahankan centang putih di tengah. Ada saran untuk itu?
Farooq Arshed
3
Kode dalam komentar Ben sekarang sudah tidak digunakan lagi. Sebagai gantinya, Anda bisa menggunakan int iColor = ContextCompat.getColor(context, R.color.primary);.
larangan geoengineering
jawaban yang brilian !! Terima kasih semuanya!
Kaveesh Kanwal
@ Mike Bukit Ok, menjelaskan mengapa Anda menempatkan lebih dari 20 colors.You perlu memasukkan greather kemudian dua puluh warna dalam array karena yang lain itu crash dengan java.lang.ArrayIndexOutOfBoundsException
AlexPad
50

Saya juga menggunakan ImageViewikon (di ListViewlayar pengaturan atau). Tapi saya pikir ada cara yang lebih sederhana untuk melakukan itu.

Gunakan tintuntuk mengubah overlay warna pada ikon yang Anda pilih.

Dalam xml,

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

berfungsi dengan baik karena itu berasal AppCompat

sud007
sumber
3
Bekerja seperti pesona! Sederhana dan sempurna. Ini perlu ditandai sebagai jawaban yang diterima.
Naga Mallesh Maddali
2
Ada banyak jawaban bagus di sini, tetapi untuk pertanyaan OP ini adalah solusi terbaik dan paling sederhana.
Sebastian Breit
untuk api 22 ke atas
philip oghenerobo balogun
1
@philipoghenerobobalogun saya melihat ini bekerja pada api 19
Jemshit Iskenderov
41

Anda harus melakukan ini untuk semua API:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);
hoangtu23
sumber
Ini memecahkan masalah dengan cara yang dapat diterima. Tetapi ketika menyaring warna, mungkin saja terjadi (itu terjadi pada saya) bahwa warna yang dihasilkan tidak seperti yang diharapkan. Warna yang mencerahkan. Apa yang saya lakukan adalah: `LightingColorFilter baru (Color.parseColor (" # FF000000 "), myFinalColor)`
Yoraco Gonzales
1
Menekankan apa yang saya pikir komentator sebelumnya katakan, solusi ini mengubah warna jika 2 parameter dalam LightingColorFilter berbeda, misalnya, ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY);akan berubah hitam menjadi abu-abu di drawable.
hBrent
1
Ini tampaknya tidak berfungsi ketika alpha digunakan untuk warna warna.
ypresto
30

Saya bisa melakukan ini dengan kode berikut, yang diambil dari suatu kegiatan (tata letak adalah yang sangat sederhana, hanya mengandung ImageView, dan tidak diposting di sini).

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;
}
Matt McMinn
sumber
dari mana saya mendapatkan Threshold atau FROM_COLOR?
mikepenz
Itu hanya konstanta yang saya definisikan; Saya baru saja mengedit jawaban untuk memasukkan mereka.
Matt McMinn
terima kasih;) mencoba tetapi tidak sesuai dengan masalah yang saya miliki. mencoba setColorFilter, dan ini berhasil tetapi ada masalah dengan penskalaan gambar .9.png. jadi jika Anda punya ide mengapa, silakan jawab pertanyaan saya. stackoverflow.com/questions/5884481/…
mikepenz
1
Filter warna jauh lebih mudah.
afollestad
17

Anda dapat menyelesaikannya menggunakan perpustakaan dukungan 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))
Ricard
sumber
1
@AmitabhaBiswas Mengapa Anda benar-benar mengubah jawaban saya yang salah? Bagian demi bagian. 1. getResources (). GetDrawable () sudah usang !! 2. Saya menggunakan perpustakaan dukungan karena saya tidak ingin peduli dengan versi Andorid Api. 3. Saya tidak ingin menggambar ulang Drawable .... Jika Anda ingin menunjukkan pendekatan lain, tulis jawaban Anda sendiri.
Ricard
1
@AmitabhaBiswas Selain itu, drawable dibagikan di antara semua getDrawable dari sumber daya, maka mutate()panggilan tersebut diperlukan untuk dapat mengubah warna drawable, tanpa mengubah semua drawable associates dengan ID sumber daya itu.
Ricard
1
Ini jawaban terbaik! Membungkus drawable dalam tampilan gambar tidak menyelesaikan pertanyaan.
Julius
15

Dalam Aktivitas Anda, Anda dapat mewarnai sumber gambar PNG Anda dengan satu warna:

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

Sekarang ketika Anda menggunakan R.drawable. * Itu harus diwarnai dengan warna yang diinginkan. Jika Anda membutuhkan warna tambahan maka Anda harus dapat .mutate () gambar.

David Douglas
sumber
4
view.getDrawable().mutate().setColorFilter(0xff777777, PorterDuff.Mode.MULTIPLY); 

Terima kasih kepada @sabadow

Hamidreza Sadegh
sumber
setColorFilter tidak digunakan lagi
Mohammad Sommakia
4

Jika Anda memiliki set yang dapat ditarik ke ImageView, Anda dapat melakukannya dengan 1 liner:

yourImageView.setColorFilter(context.getResources().getColor(R.color.YOUR_COLOR_HERE);
Martin Nowosad
sumber
3

Lihat kode contoh ini " 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 yang relevan tersedia di sini :

Adrian
sumber
1
Ini memang menunjukkan cara menggunakan ColorMatrix, tapi saya tidak melihat cara menggunakannya untuk mendapatkan hasil yang saya cari.
Matt McMinn
3

Ini bekerja dengan semuanya dengan latar belakang:

Tampilan teks, Tombol ...

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

Cuplikan kode ini berfungsi untuk saya:

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

imgView.getDrawable().setColorFilter(porterDuffColorFilter);
imgView.setBackgroundColor(Color.TRANSPARENT)
Mehatab
sumber
2

Ada begitu banyak solusi tetapi tidak ada yang menyarankan jika file xml sumber daya warna sudah memiliki warna maka kita dapat memilih langsung dari sana juga seperti di bawah ini:

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

Contoh singkat untuk mengubah warna yang dapat digambar sesuai isWorkingbidang.

Bentuk saya 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>

Metode saya untuk berubah:

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

Contoh penggunaan:

text1.setCompoundDrawablesWithIntrinsicBounds(getColoredDrawable(R.drawable.shape, isworking()), null, null, null);
ikan mati
sumber
0
Int color = Color.GRAY; 
// or int color = Color.argb(123,255,0,5);
// or int color = 0xaaff000;

dalam XML /res/values/color.xml

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

Kode Java

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

GradientDrawable drawableBg = yourView.getBackground().mutate();
drawableBg.setColor(color);
Yang Mulia
sumber
0

Terlambat tetapi jika seseorang membutuhkannya:

   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
        }
    }
Mouaad Abdelghafour AITALI
sumber
0

Ini berfungsi untuk beberapa drawable sederhana. Saya menggunakannya pada bentuk persegi warna solid sederhana dengan sudut bulat dan perlu mengubah warna itu dengan tata letak yang berbeda.

Coba ini

android:backgroundTint="#101010"
Jay Parikh
sumber
-1

Ini sangat sangat sederhana ketika Anda menggunakan perpustakaan untuk melakukannya untuk Anda. Coba pustaka ini

Anda dapat memanggil seperti ini:

Icon.on(holderView).color(R.color.your_color).icon(R.mipmap.your_icon).put();
Vansuita Jr.
sumber