在 API29上不推荐使用 setColorFilter

我使用以下行来更改 VectorDrawable 的颜色:

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

虽然现在已经不再使用它了,但是它工作得很好。文档建议我使用:

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

不过,BlendModeColorFilter只能在 API29上使用。在检查了这个不推荐的方法的源代码之后,我意识到它调用:

new PorterDuffColorFilter()

所以,我继续用:

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

着色起作用了。这是否是废弃方法的正确替换,或者我必须在 API29上使用 BlendModeColorFilter?

谢谢你。

39346 次浏览

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)

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.

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

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)

BlendMode Filters may require higher API to be working.

check out this link to know more