Как изменить цвета Drawable в Android?

271

Я работаю над приложением для Android, и у меня есть чертеж, который я загружаю из исходного изображения. На этом изображении я хотел бы преобразовать все белые пиксели в другой цвет, скажем синий, и затем кэшировать результирующий объект Drawable, чтобы я мог использовать его позже.

Например, скажем, у меня есть PNG-файл размером 20x20 с белым кружком посередине, и все, что находится вне круга, прозрачно. Какой лучший способ превратить этот белый круг в синий и сохранить результаты в кеше? Изменится ли ответ, если я захочу использовать исходное изображение для создания нескольких новых Drawables (скажем, синего, красного, зеленого, оранжевого и т. Д.)?

Я предполагаю, что я хочу использовать ColorMatrix в некотором роде, но я не уверен, как.

Мэтт МакМинн
источник
2
Вы наконец-то получили это как-то? Ниже я вижу много ответов, из которых я тоже много пробовал, но ничего не получается. В настоящее время у меня есть белый квадрат, который я бы хотел каждый раз менять по цвету в зависимости от потребностей, чтобы мне не приходилось создавать статические активы. Просьба предложить, так как я все еще жду рабочего решения для моей простой формы в белом цвете.
omkar.ghaisas
@ omkar.ghaisas Я создал библиотеку под названием SillyAndroid, которая содержит универсальный класс Coloring и выполняет всевозможные раскраски для рисования и текста. Вы можете проверить это на github.com/milosmns/silly-android . Класс находится по адресу/sillyandroid/src/main/java/me/angrybyte/sillyandroid/extras/Coloring.java
milosmns

Ответы:

221

Я думаю, что вы можете просто использовать Drawable.setColorFilter( 0xffff0000, Mode.MULTIPLY ). Это установит белые пиксели на красный, но я не думаю, что это повлияет на прозрачные пиксели.

Смотрите Drawable # setColorFilter

thom_nic
источник
9
Это будет хорошо работать, когда оттягиваемый цвет - один, лучше - белый.
Митул Накум
67
Если цвет изменяется динамически (например, в адаптере), рисунок должен быть изменчивым. Пример: Drawable.mutate().setColorFilter( 0xffff0000, Mode.MULTIPLY)больше информации: curious-creature.org/2009/05/02/drawable-mutations
sabadow
1
Да, это особенно хорошо для выделения (светлее, темнее или добавления оттенка к изображению в оттенках серого). Я использую этот трюк для переключения кнопок, где «unchecked» - это оттенки серого, а «флажок» - жирный цвет из цветовой палитры моего приложения. Лично я нахожу это проще, чем пользовательский флажок.
thom_nic
2
Это именно то, что я искал, хотя это невероятно раздражает, что мы не можем сделать это в XML ( за исключением 5.0+ ). Тонировка даже недоступна в AppCompat, поэтому мы застряли при необходимости звонить setColorFilterкаждый раз, когда используем значки, а не селекторы с разными оттенками цвета. Тем не менее, это гораздо лучшее решение, чем непосредственное редактирование png и наличие дополнительных статических ресурсов.
Крис Cirefice
21
Умножение не будет работать, если значок вашего источника имеет темный цвет. Чтобы закрасить исходную иконку цветом назначения, используйте SRC_IN: myImage.getDrawable().mutate().setColorFilter(getResources().getColor(R.color.icon_grey), PorterDuff.Mode.SRC_IN);
Distwo
152

Попробуйте этот код:

ImageView lineColorCode = (ImageView)convertView.findViewById(R.id.line_color_code);
int color = Color.parseColor("#AE6118"); //The color u want             
lineColorCode.setColorFilter(color);
Naren
источник
106

Я знаю, что этот вопрос задавался задолго до Lollipop, но я хотел бы добавить хороший способ сделать это на Android 5. +. Вы делаете отрисовываемый XML-файл, который ссылается на исходный, и задаете для него оттенок:

<?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"/>
MinceMan
источник
эта часть последней библиотеки поддержки тоже?
S-K '
Это помогает только с несколькими простыми виджетами.
MinceMan
8
Оттенок в support-v4 через DrawableCompat
Марк Ренуф
1
Круто, я посмотрю на это и обновлю это соответственно.
MinceMan
Фреска не поддерживает этот тип
рисования
63

Новая поддержка v4 возвращает оттенок к API 4.

ты можешь сделать это так

public static Drawable setTint(Drawable d, int color) {
    Drawable wrappedDrawable = DrawableCompat.wrap(d);
    DrawableCompat.setTint(wrappedDrawable, color);
    return wrappedDrawable;
}
Пей
источник
2
Начиная с библиотеки поддержки 22.
rnrneverdies
1
Это предпочтительное решение, так как тонировка рисования была серой областью в старых API с момента выпуска lollipop. Это тормозит этот барьер! Я не знал об этом - спасибо @Pei
RicardoSousaDev
2
Быть осторожен! Чтобы избежать проблем, связанных с состоянием, вы должны мутировать новую обернутую отрисовку "#mutate ()". См. Stackoverflow.com/a/44593641/5555218
Рикард
62

Если у вас есть сплошной цвет для рисования и вы хотите изменить его на другой сплошной цвет, вы можете использовать a ColorMatrixColorFilter. Прозрачность сохраняется.

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
Если вы хотите использовать цветовой ресурс, а не строку (# ff0000 и т. Д.), Вы можете использовать, например, int iColor = getResources (). GetColor (R.color.primary)
Бен Клейтон
это работает, но у меня есть флажок, и я хочу сохранить белую галочку в середине. Любое предложение для этого?
Фарук Аршед
3
Код в комментарии Бена теперь устарел. Вместо этого вы можете использовать int iColor = ContextCompat.getColor(context, R.color.primary);.
запрет геоинженерии
блестящий ответ !! Спасибо всем!
Kaveesh Kanwal
@Mike Hill Хорошо, объясните, почему вы добавили более 20 цветов. Вам нужно вставить больше, чем двадцать цветов в массиве, потому что иначе он вылетает с java.lang.ArrayIndexOutOfBoundsException
AlexPad
50

Я также использую ImageViewдля значков (вListView или экран настроек). Но я думаю, что есть гораздо более простой способ сделать это.

использование tint чтобы изменить цвет наложения на выбранный значок.

В xml,

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

работает нормально, так как это происходит из AppCompat

sud007
источник
3
Работает как шарм! Просто и идеально. Это должно быть отмечено как принятый ответ.
Нага Маллеш Маддали
2
Здесь есть много хороших ответов, но для вопроса ОП это лучшее и самое простое решение.
Себастьян Брейт
для API 22 и выше
Филип Огенеробо Balogun
1
@philipoghenerobobalogun я видел, как это работает над API 19
Джемшит Искендеров
41

Вы должны сделать это для всех API:

Drawable myIcon = getResources().getDrawable( R.drawable.button ); 
ColorFilter filter = new LightingColorFilter( Color.BLACK, Color.BLACK);
myIcon.setColorFilter(filter);
hoangtu23
источник
Это решило проблему приемлемым образом. Но при фильтрации цвета может случиться (это случилось со мной), что полученный цвет не соответствует ожидаемому. Цвет, который должен был осветлить. Я сделал следующее: `new LightingColorFilter (Color.parseColor (" # FF000000 "), myFinalColor)`
Йорако Гонзалес
1
Подчеркивая, что я думаю, что предыдущий комментатор говорил, это решение меняет цвета, если 2 параметра в LightingColorFilter отличаются, например, ColorFilter filter = new LightingColorFilter(Color.BLACK, Color.LTGRAY); изменится с черного на серый в чертеже.
hBrent
1
Это кажется не работает, когда альфа используется для оттенка цвета.
Ипресто
30

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

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;
}
Мэтт МакМинн
источник
откуда я могу получить порог или FROM_COLOR?
Микепенз
Это были просто константы, которые я определил; Я просто отредактировал ответ, чтобы включить их.
Мэтт МакМинн
спасибо;) попробовал, но это не соответствует моей проблеме. попробовал setColorFilter, и это работает, но есть проблема с масштабированием изображения .9.png. так что если у вас есть идея, почему, пожалуйста, ответьте на мой вопрос. stackoverflow.com/questions/5884481/…
mikepenz
1
Цветовые фильтры намного проще.
Афоллестад
17

Вы можете решить эту проблему, используя совместимые библиотеки поддержки 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
источник
1
@AmitabhaBiswas Почему вы полностью изменили неправильный мой ответ? По частям. 1. getResources (). GetDrawable () устарела !! 2. Я пользуюсь вспомогательными библиотеками, потому что не хочу заботиться о версиях Andorid Api. 3. Я не хочу перерисовывать Drawable .... Если вы хотите показать другой подход, напишите свой собственный ответ.
Рикард
1
@AmitabhaBiswas Кроме того, объекты рисования распределяются между всеми getDrawable из ресурсов, поэтому mutate()требуется, чтобы вызов мог изменить оттенок объекта рисования, не изменяя все ассоциации объектов рисования с этим идентификатором ресурса.
Рикард
1
Это лучший ответ! Обтекание нарисованного в представлении изображения не решает вопрос.
Юлий
15

В своей деятельности вы можете подкрасить ресурсы изображений PNG одним цветом:

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

Теперь, когда вы используете R.drawable. * Он должен быть окрашен в желаемый оттенок. Если вам нужны дополнительные цвета, вы можете использовать .mutate () для рисования.

Дэвид Дуглас
источник
4
view.getDrawable().mutate().setColorFilter(0xff777777, PorterDuff.Mode.MULTIPLY); 

Благодаря @sabadow

Хамидреза Садег
источник
setColorFilter устарела
Мохаммед Соммакия
4

Если вы установили свой Drawable в ImageView, вы можете сделать это с 1 вкладышем:

yourImageView.setColorFilter(context.getResources().getColor(R.color.YOUR_COLOR_HERE);
Мартин Новосад
источник
3

Проверьте этот пример кода " 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 доступен здесь :

Адриан
источник
1
Это показывает, как использовать ColorMatrix, но я не вижу, как использовать его, чтобы получить результаты, которые я ищу.
Мэтт Макминн
3

Это работает со всем с фоном:

Textview, кнопка ...

TextView text = (TextView) View.findViewById(R.id.MyText);
text.setBackgroundResource(Icon);    
text.getBackground().setColorFilter(getResources().getColor(Color), PorterDuff.Mode.SRC_ATOP);
тони
источник
3

Этот фрагмент кода работал для меня:

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

imgView.getDrawable().setColorFilter(porterDuffColorFilter);
imgView.setBackgroundColor(Color.TRANSPARENT)
Mehatab
источник
2

Есть так много решений, но никто не предложил, если XML- файл цветового ресурса уже имеет цвет, тогда мы можем выбрать непосредственно оттуда также, как показано ниже:

ImageView imageView = (ImageView) findViewById(R.id.imageview);
imageView.setColorFilter(getString(R.color.your_color));
Панкай
источник
1

Краткий пример изменения цвета рисования в соответствии с isWorking полем.

Моя форма 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>

Мой способ изменить:

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

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

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;

в XML /res/values/color.xml

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

Java-код

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

GradientDrawable drawableBg = yourView.getBackground().mutate();
drawableBg.setColor(color);
Ven Ren
источник
0

Слишком поздно, но в случае, если это кому-то нужно:

   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

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

Попробуй это

android:backgroundTint="#101010"
Джей Парих
источник
-1

Это очень очень просто, когда вы используете библиотеку, чтобы сделать это для вас. Попробуй это библиотеку

Вы можете позвонить так:

Icon.on(holderView).color(R.color.your_color).icon(R.mipmap.your_icon).put();
Вансуита-младший
источник