setColorFilter is deprecated on API29

Android

Android Problem Overview


I use the following line to change the color of a VectorDrawable:

mydrawable.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_ATOP)

This works nice, though it is now deprecated. The documentation suggests that I use:

mydrawable.getBackground().setColorFilter(new BlendModeColorFilter(color, PorterDuff.Mode.SRC_ATOP))

Though, BlendModeColorFilter is only available on API29. After examining the source of the deprecated method, I have realized that it calls:

new PorterDuffColorFilter()

So, I went ahead and used:

mydrawable.getBackground().setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP))

The coloring worked. Is this the right replacement for the deprecated method or I must use BlendModeColorFilter on API29?

Thank you.

Android Solutions


Solution 1 - Android

Try this:

public class MyDrawableCompat {
    public static void setColorFilter(@NonNull Drawable drawable, @ColorInt int color) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            drawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.SRC_ATOP));
        } else {
            drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
        }
    }
}

And this:

MyDrawableCompat.setColorFilter(mydrawable.getBackground(), color);

UPDATE: Just use the latest version of the core androidx library and this code:

mydrawable.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP)

Solution 2 - Android

Use androidx.core:core:1.2.0 or androidx.core:core-ktx:1.2.0.

// Java
implementation 'androidx.core:core:1.2.0'
// Kotlin
implementation 'androidx.core:core-ktx:1.2.0'

And this:

drawable.setColorFilter(BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP))

Solution 3 - Android

Thanks to @shmakova I added a solution for Kotlin.

import android.graphics.BlendMode
import android.graphics.BlendModeColorFilter
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.os.Build
import androidx.annotation.RequiresApi

fun Drawable.setColorFilter(color: Int, mode: Mode = Mode.SRC_ATOP) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        colorFilter = BlendModeColorFilter(color, mode.getBlendMode())
    } else {
        @Suppress("DEPRECATION")
        setColorFilter(color, mode.getPorterDuffMode())
    }
}

// This class is needed to call the setColorFilter 
// with different BlendMode on older API (before 29).
enum class Mode {
    CLEAR,
    SRC,
    DST,
    SRC_OVER,
    DST_OVER,
    SRC_IN,
    DST_IN,
    SRC_OUT,
    DST_OUT,
    SRC_ATOP,
    DST_ATOP,
    XOR,
    DARKEN,
    LIGHTEN,
    MULTIPLY,
    SCREEN,
    ADD,
    OVERLAY;

    @RequiresApi(Build.VERSION_CODES.Q)
    fun getBlendMode(): BlendMode =
        when (this) {
            CLEAR -> BlendMode.CLEAR
            SRC -> BlendMode.SRC
            DST -> BlendMode.DST
            SRC_OVER -> BlendMode.SRC_OVER
            DST_OVER -> BlendMode.DST_OVER
            SRC_IN -> BlendMode.SRC_IN
            DST_IN -> BlendMode.DST_IN
            SRC_OUT -> BlendMode.SRC_OUT
            DST_OUT -> BlendMode.DST_OUT
            SRC_ATOP -> BlendMode.SRC_ATOP
            DST_ATOP -> BlendMode.DST_ATOP
            XOR -> BlendMode.XOR
            DARKEN -> BlendMode.DARKEN
            LIGHTEN -> BlendMode.LIGHTEN
            MULTIPLY -> BlendMode.MULTIPLY
            SCREEN -> BlendMode.SCREEN
            ADD -> BlendMode.PLUS
            OVERLAY -> BlendMode.OVERLAY
        }

    fun getPorterDuffMode(): PorterDuff.Mode =
        when (this) {
            CLEAR -> PorterDuff.Mode.CLEAR
            SRC -> PorterDuff.Mode.SRC
            DST -> PorterDuff.Mode.DST
            SRC_OVER -> PorterDuff.Mode.SRC_OVER
            DST_OVER -> PorterDuff.Mode.DST_OVER
            SRC_IN -> PorterDuff.Mode.SRC_IN
            DST_IN -> PorterDuff.Mode.DST_IN
            SRC_OUT -> PorterDuff.Mode.SRC_OUT
            DST_OUT -> PorterDuff.Mode.DST_OUT
            SRC_ATOP -> PorterDuff.Mode.SRC_ATOP
            DST_ATOP -> PorterDuff.Mode.DST_ATOP
            XOR -> PorterDuff.Mode.XOR
            DARKEN -> PorterDuff.Mode.DARKEN
            LIGHTEN -> PorterDuff.Mode.LIGHTEN
            MULTIPLY -> PorterDuff.Mode.MULTIPLY
            SCREEN -> PorterDuff.Mode.SCREEN
            ADD -> PorterDuff.Mode.ADD
            OVERLAY -> PorterDuff.Mode.OVERLAY
        }
}

Use it as usually:

toolbar?.navigationIcon?.setColorFilter(ContextCompat.getColor(this, color)) /* 1 */
progressBar.indeterminateDrawable.setColorFilter(color, Mode.SRC_IN) /* 2 */

I tried to call setColorFilter with both BlendMode and PorterDuff.Mode parameters (like drawable.setColorFilter(color, BlendMode.SRC_ATOP, PorterDuff.Mode.SRC_ATOP)), but that led to a runtime exception:

> java.lang.NoClassDefFoundError: Failed resolution of: > Landroid/graphics/BlendMode;

So, we can call any method with BlendMode only starting from SDK version 29 (it was added there). I had to create setColorFilter with Mode parameter.

Solution 4 - Android

PorterDuffColorFilter was added in API level 1 and it's not deprecated, in addition PorterDuffColorFilter is a subclass of ColorFilter. You may use it as you did with no problems as setColorFilter parameter is of type ColorFilter and it does not require you to pass a BlendModeColorFilter. The are many reasons why the documentation may suggests BlendModeColorFilter. Maybe they plan on deprecating PorterDuffColorFilter in the future (as of 2021 it is still not deprecated) or maybe it just performs better, who knows... The point is that you used it correctly. If you want to safeguard against your code breaking down because PorterDuffColorFilter suddenly got deprecated and removed then you should do what many others have suggested and add a Version check. If you are not worried about that then you don't have to do it but you will have to keep an eye out for any future changes.

But all in all as of 2021 your code will work on all versions.

Regards

NOTE: setColorFilter(int color, PorterDuff.Mode mode) is deprecated this answer is only relevant to setColorFilter(ColorFilter colorFilter)

Solution 5 - Android

BlendMode Filters may require higher API to be working.

check out this link to know more

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMigrata NomosView Question on Stackoverflow
Solution 1 - AndroidshmakovaView Answer on Stackoverflow
Solution 2 - AndroidWataru MukainakanoView Answer on Stackoverflow
Solution 3 - AndroidCoolMindView Answer on Stackoverflow
Solution 4 - AndroidOsagui AghedoView Answer on Stackoverflow
Solution 5 - AndroidHassan El khalifteView Answer on Stackoverflow