`context.getColor` 中的 `theme` 有什么用?

What's the use of `theme` in `context.getColor` ?

背景

从API 23开始,弃用了获取资源颜色getColor(只需给它资源id)的小方法:

相反,我们被告知要使用也包含 Theme 参数的方法:

This method was deprecated in API level 23. Use getColor(int, Theme) instead.

https://developer.android.com/reference/android/content/res/Resources.html#getColor(int, android.content.res.Resources.Theme)

问题

The docs不多说theme:

https://developer.android.com/reference/android/content/res/Resources.html#getColor(int, android.content.res.Resources.Theme)

我发现了什么

在网上搜索,我只能找到我们可以使用支持库来获取颜色:

ContextCompat.getColor(context, R.color.color_name);

这有点奇怪,因为在引擎盖下,它似乎没有对主题做任何事情。这是它的代码:

@ColorInt
    public static final int getColor(@NonNull Context context, @ColorRes int id) {
        if (Build.VERSION.SDK_INT >= 23) {
            return context.getColor(id);
        } else {
            return context.getResources().getColor(id);
        }
    }

查看Context.getColor,我可以看到:

所以在我看来它使用的是 activity?

的主题

问题

  1. 'theme'参数的作用是什么?

  2. 如何使用?有什么sample/tutorial/article吗?

  3. 为什么这个函数还是被弃用了?对我来说似乎仍然可以安全使用...

  4. 支持库函数有什么用?它与使用已弃用的功能有何不同?

tl;dr

  • 如果你的最小 SDK 是 23 你不需要兼容 API 来获取颜色。
  • 如果您不在颜色中使用主题属性引用并且不打算更改,请使用Context.getResources().getColor(int)getColorStateList(int)到。它被标记为已弃用,但在功能上没问题。
  • 如果您只想在 API 23+ 上使用颜色主题属性引用,请使用 ContextCompat.getColorStateList(Context, int).
  • 如果您使用 AppCompat 并希望能够在 Android 的所有版本上使用主题属性引用,请使用 AppCompatResources.getColorStateList(Context, int).

开始 API 23 ColorStateList 可以包含 主题属性引用 ,与 Drawable 自 API 以来允许的相同内容21.

主题属性主要用于通过 getColorStateList 方法获得的颜色状态列表,而不是 getColor,所以我将从现在开始使用它。提到的大部分内容都适用于这两种变体。

示例:

res/color/my_csl.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false" android:color="?attr/colorControlHighlight" android:alpha="?android:disabledAlpha"/>
    <item android:color="?attr/colorControlHighlight"/>
</selector>

What's the purpose of the 'theme' parameter?

需要主题才能解析主题属性。资源本身并不知道任何主题。主题由上下文提供。

context.getColorStateList(R.color.my_csl);

本质上是

的捷径
context.getResources().getColorStateList(R.color.my_csl, context.getTheme());

这两种方法都是 public API 的一部分,因此它们都被向后移植:

ContextCompat.getColorStateList(context, R.color.my_csl);
ResourcesCompat.getColorStateList(context.getResources(), R.color.my_csl, context.getTheme());

How is it used?

通常就这么简单:

final int myColor = ContextCompat.getColorStateList(context, R.color.my_csl);

如果您使用 AppCompat,您最好选择其他选项:

final int myColor = AppCompatResources.getColorStateList(context, R.color.my_csl);

以下是这两个选项之间的比较:

  • ContextCompat.getColorStateList

    • 仅需要 support-compat 支持库
    • 仅向后移植 API - 尝试解析低于 API 23
    • 的主题属性时会崩溃
  • AppCompatResources.getColorStateList

    • 需要完整的 appcompat-v7 支持库
    • Backports 功能 - 即使低于 API23
    • ,它也会尊重上下文主题

ResourcesCompat.getColorStateList(int, Theme)Resources.getColorStateList(int, Theme) 的 API(非功能性)向后移植,由 Context.getColorStateList(int) 在 API 23+ 上内部使用。在日常使用中,您将不需要此方法。

Why was the function deprecated anyway? It still seems safe to use for me...

该方法已被弃用,以便将开发人员从不了解主题的版本迁移到该方法的主题可识别版本。

theme-unaware 方法仍然可以完全安全地使用,只要您使用它来获取没有主题属性引用的颜色,例如:

res/values/colors.xml

<resources>
    <color name="my_cint">#f00</color>
</resources>

可以通过

安全获得
context.getResources().getColor(R.color.my_cint);

What's the use of the support library function?

ContextCompat.getColorStateList(context, R.color.my_csl);

字面意思就是这个

public static final ColorStateList getColorStateList(Context context, @ColorRes int id) {
    if (Build.VERSION.SDK_INT >= 23) {
        return context.getColorStateList(id);
    } else {
        return context.getResources().getColorStateList(id);
    }
}

该方法已存在,因此您不必在任何地方都编写 if-else。就是这样。

在API 23+可以使用上下文主题。 API23以下退回老版本无法解析主题属性

ResourcesCompat.getColorStateList(int, Theme) 看起来和工作方式相似,它忽略 API 23.

以下的主题属性(使用时会崩溃)

AppCompatResources.getColorStateList(Context, int) 不会崩溃并正确解析 Android.

所有版本的主题属性

What about ResourcesCompat#getColorStateList?

您通常不需要此方法。

这是一个抽象层。它告诉你,要解析一种颜色,你不需要任何上帝对象,任何 ContextResources 刚好可以解析一个颜色。但是为了 Resources 能够解析主题属性,您需要提供 Theme。可以从 Context 获得 Theme 这一事实与 Resources 无关。 Resources 不会透露或关心它本身来自某些 Context.getResources()

Can you [...] update about getColor too?

getColor 将解决

  • <color> 颜色整数资源
  • <selector> 颜色状态列表资源作为其默认颜色的颜色整数

getColorStateList 将解决

  • <color> 资源为 ColorStateList 具有一种颜色
  • <selector> CSL 资源作为 ColorStateList 具有指定的颜色

使用消费者需要的 API。

没有 AppCompatResources.getColor(Context, int) 因为颜色 资源 被定义为主题属性参考没有什么意义。如果你需要这个 AppCompatResources.getColorStateList(Context, int).getDefaultColor() 是功能等价物。

ContextCompat.getColor(Context, int)ResourcesCompat.getColor(Resources, int, Theme) 存在是因为它们反映了框架 API。如上所述,它们的实际用途值得商榷。

我建议阅读下面的讨论以掌握一些难以捉摸的细微差别。

我经常使用 fragments,通过玩弄这就是发现的东西。 首先设置一个上下文变量如下:

private lateinit var  contextX: Context

然后实例化上下文如下:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
contextX=requireContext() ...... 

然后使用 contextX 将颜色分配给视图

binding.veeGroupId.setBackgroundColor(getColor(contextX,R.color.yellow_light))

这很好用。所需的导入应由系统自动生成。

使这项工作生效的导入,它们应该会自动加载,但当您 select 提示时要小心。

import android.content.Context
import androidx.core.content.ContextCompat.getColor