【问题标题】:setColorFilter is deprecated on API29setColorFilter 在 API29 上已弃用
【发布时间】:2019-11-05 01:14:42
【问题描述】:

我使用下面这行来改变 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?

谢谢。

【问题讨论】:

  • 您可以使用条件逻辑来检查用户的设备是否在 API 29 及更高版本上,并使用该颜色过滤器,如果条件不满足,则使用您使用的以前的代码。
  • @Edric,您的回复。是的,我可以检查,但这需要检查 100 多个实例或对静态方法执行着色,然后将其链接到所有实例。如果使用 PorterDuffColorFilter 是正确的,我可以轻松地进行批量替换。
  • 如果您只是在可绘制对象上应用纯色而不进行任何转换,那么您也可以使用:DrawableCompat.setTint(drawable, color) 或直接从 xml 为 ImageViews 为可绘制对象着色例子。
  • 感谢您的回复,@AndyRes。我很感激。虽然,我不应用色调,但我完全替换了颜色。我创建了一个 VectorDrawable,并根据活动用不同的颜色为它着色。
  • OK.. 我不明白为什么那行不通。来自文档:setTint() - a Drawable's drawing content will be blended together with its tint before it is drawn to the screen. This functions similarly to {@link #setColorFilter(int, PorterDuff.Mode)}.

标签: android


【解决方案1】:

试试这个:

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

还有这个:

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

更新: 只需使用最新版本的core androidx library 和此代码:

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

【讨论】:

  • 感谢您花时间编写方法,shmakova。我很感激。如果我无法验证 new PorterDuffColorFilter() 是否正常工作,这就是我必须要做的。在使用它时,我必须手动替换每个条目,并且我有 100 多个条目。我希望有人能对new PorterDuffColorFilter()有所了解
  • drawable.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, BlendModeCompat.SRC_ATOP) 只需一行代码
  • @ZbarceaChristian 你是对的。这是正确的答案。执行 if/else 仍会在构建日志中显示警告。
  • 一些BlendModeCompat 值实际上被标记为需要高API,而PorterDuff 不需要。示例是 MULTIPLY 。怎么会?
  • @androiddeveloper 你找到MULTIPLY的解决方案了吗??
【解决方案2】:

感谢@shmakova,我为 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
        }
}

照常使用:

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

我尝试使用BlendModePorterDuff.Mode 参数(如drawable.setColorFilter(color, BlendMode.SRC_ATOP, PorterDuff.Mode.SRC_ATOP))调用setColorFilter,但这导致了运行时异常:

java.lang.NoClassDefFoundError:解析失败: Landroid/graphics/BlendMode;

所以,我们可以使用BlendMode 调用任何方法,仅从 SDK 版本 29 开始(它已添加到那里)。我必须使用Mode 参数创建setColorFilter

【讨论】:

  • 我已经在一个大型项目中对此进行了测试,并且运行成功。我创建了一个单独的 Kotlin 文件并从那里调用它,这实质上为满足这些参数时运行的原始文件创建了一个备用 setColorFilter。
  • @TitanKing,感谢您的反馈!你还需要使用这种扩展方法吗?你知道,我已经使用了几个月,但后来转向 Wataru Mukainakano 解决方案。
【解决方案3】:

使用androidx.core:core:1.2.0androidx.core:core-ktx:1.2.0

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

还有这个:

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

【讨论】:

  • 谨慎使用此替代方法。我先尝试了这个,但是使用Multiply 混合模式时结果与mydrawable.getBackground().setColorFilter(color, PorterDuff.Mode.MULTIPLY) 不匹配。此解决方案似乎效果最好并保持相同的预期视觉效果:mydrawable.getBackground().setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY))
  • @BrianBegun 感谢您的通知!当我阅读BlendModeUtils.obtainPorterDuffFromCompat 的源代码时,确实如此。对不起。显然它似乎与BlendModeCompat.MODULATE 的行为相同。映射如下。 case MODULATE: return PorterDuff.Mode.MULTIPLY;BlendModeUtils.java
  • 啊,感谢您澄清@WataruMukainakano。奇怪的是他们将MODULATE 映射到MULTIPLY。不知道我明白为什么这样做。不管怎样,谢谢你填补了这个空白。非常感谢。
  • 还有一点要补充。我查看了 BlendModeUtils 的源代码(位于此处:android.googlesource.com/platform/frameworks/support/+/refs/… 似乎 MULTIPLY 映射到 MULTIPLY 用于 API29(仅)使用 BlendMode 否则对于所有其他 API MODULATE 映射到 MULTIPLY使用PorterDuff.Mode
  • 谷歌是否与某人签订了合同来打扰我们? :|为什么它让一切变得复杂?
【解决方案4】:

PorterDuffColorFilter 是在 API 级别 1 中添加的,并且未被弃用,此外,PorterDuffColorFilterColorFilter 的子类。您可以毫无问题地使用它,因为setColorFilter 参数的类型为ColorFilter,并且它不需要您传递BlendModeColorFilter。文档可能建议BlendModeColorFilter 的原因有很多。也许他们计划在未来弃用 PorterDuffColorFilter(到 2021 年它仍然没有被弃用)或者它可能只是表现更好,谁知道......关键是你正确地使用了它。如果您想防止您的代码因为PorterDuffColorFilter 突然被弃用和删除而崩溃,那么您应该按照许多其他人的建议进行操作并添加版本检查。如果您对此不担心,则不必这样做,但您必须留意未来的任何变化。

但总的来说,到 2021 年,您的代码将适用于所有版本。

问候

注意:setColorFilter(int color, PorterDuff.Mode mode) 已弃用此答案仅与 setColorFilter(ColorFilter colorFilter) 相关

【讨论】:

    【解决方案5】:

    BlendMode 过滤器可能需要更高的 API 才能工作。

    查看此link 了解更多信息

    【讨论】:

      猜你喜欢
      • 2011-04-01
      • 2010-10-08
      • 2011-08-10
      • 2014-02-12
      • 1970-01-01
      • 2022-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多