setColorFilter is deprecated on API29
AndroidAndroid 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