ResourcesCompat.getDrawable() 与 AppCompatResources.getDrawable()

Posted

技术标签:

【中文标题】ResourcesCompat.getDrawable() 与 AppCompatResources.getDrawable()【英文标题】:ResourcesCompat.getDrawable() vs AppCompatResources.getDrawable() 【发布时间】:2017-08-17 17:02:06 【问题描述】:

我对这两个 API 有点困惑。

ResourcesCompat.getDrawable(Resources res, int id, Resources.Theme theme)

返回与特定资源 ID 关联并针对指定主题设置样式的可绘制对象。将根据底层资源返回各种类型的对象——例如,纯色、PNG 图像、可缩放图像等。

在 API 级别 21 之前,不会应用主题,并且此方法只是调用 getDrawable(int)。

AppCompatResources.getDrawable(Context context, int resId)

返回与特定资源 ID 关联的可绘制对象。

此方法支持在没有平台支持的设备上对 vectoranimated-vector 资源进行膨胀。

问题

    这两个类之间的显着差异是什么 (除了向量通货膨胀)? 我应该更喜欢哪一个,为什么?

【问题讨论】:

【参考方案1】:

这是我测试后的理解

ContextCompat.getDrawable(@NonNull Context context, @DrawableRes int resId)

ResourcesCompat.getDrawable(@NonNull Resources res, @DrawableRes int id, @Nullable Theme theme)

AppCompatResources.getDrawable(@NonNull Context context, @DrawableRes int resId)

VectorDrawableCompat.create(@NonNull Resources res, @DrawableRes int resId, @Nullable Theme theme

首先看到的是VectorDrawableCompatResourcesCompat可以具体的主题

I) 不使用

Application 类的onCreated 中的AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

1) 对于矢量图像

API >= 21

ContextCompat 工作顺利 ResourcesCompat 工作顺利 AppCompatResources 工作顺利 VectorDrawableCompat 工作得很好

API

ContextCompat 崩溃 ResourcesCompat 崩溃 AppCompatResources 工作顺利 VectorDrawableCompat 工作得很好

2) 普通图片

在所有级别 ContextCompat 工作顺利 ResourcesCompat 工作顺利 AppCompatResources 工作顺利 VectorDrawableCompat 崩溃

II) 使用

Application 类的onCreated 中的AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

1) 对于矢量图像

在所有级别 ContextCompat 工作得很好 ResourcesCompat 工作顺利 AppCompatResources 工作顺利 VectorDrawableCompat 工作顺利

2) 普通图片

在所有级别 ContextCompat 工作得很好 ResourcesCompat 工作顺利 AppCompatResources 工作得很好 VectorDrawableCompat 崩溃

【讨论】:

很好的总结,但不幸的是只显示了支持库有多么混乱:)。您有 1002345 种方法来创建可绘制对象(动画或非动画)。我们应该只有一种独特的方法,如果不支持动画,他们应该尝试将其转换为简单的可绘制图像,但不要使用 ResourceNotFound 使应用程序崩溃【参考方案2】:

查看这两种方法的源代码,它们看起来非常相似。如果你没有向量,你可能会使用其中一个或另一个。

ResourcesCompat.getDrawable() 将在 API 21 或更高版本上调用 Resources#getDrawable(int, theme)。它还支持 android API 4+。仅此而已:

public Drawable getDrawable(Resources res, int id, Theme theme)
        throws NotFoundException 
    final int version = Build.VERSION.SDK_INT;
    if (version >= 21) 
        return ResourcesCompatApi21.getDrawable(res, id, theme);
     else 
        return res.getDrawable(id);
    

其中ResourcesCompatApi21 仅调用res.getDrawable(id, theme)。这意味着如果设备不支持矢量可绘制对象,它将不允许绘制矢量可绘制对象。但是,它允许您传入主题。

与此同时,AppCompatResources.getDrawable(Context context, int resId) 的代码更改最终落到了这一点:

Drawable getDrawable(@NonNull Context context, @DrawableRes int resId, boolean failIfNotKnown) 
    checkVectorDrawableSetup(context);

    Drawable drawable = loadDrawableFromDelegates(context, resId);
    if (drawable == null) 
        drawable = createDrawableIfNeeded(context, resId);
    
    if (drawable == null) 
        drawable = ContextCompat.getDrawable(context, resId);
    

    if (drawable != null) 
        // Tint it if needed
        drawable = tintDrawable(context, resId, failIfNotKnown, drawable);
    
    if (drawable != null) 
        // See if we need to 'fix' the drawable
        DrawableUtils.fixDrawable(drawable);
    

    return drawable;

因此,如果可以,此实例将尝试绘制资源,否则它会在ContextCompat 版本中查找资源。然后它甚至会在必要时对其进行着色。但是,此方法仅支持 API 7+。

所以我想决定你是否应该使用任何一个,

    您是否必须支持 API 4、5 或 6?

    是:别无选择,只能使用ResourcesCompatContextCompat。 否:继续执行 #2。

    您绝对需要提供自定义主题吗?

    是:别无选择,只能使用ResourcesCompat 否:使用AppCompatResources

【讨论】:

“您绝对需要提供自定义主题吗?是的:别无选择,只能使用 ResourcesCompat”谢谢您解决这个问题。 AppCompatResources 的图片质量(分辨率)对于可绘制矢量的资源来说更好。【参考方案3】:

上下文兼容性

ResourcesCompatContextCompat 以及 support-v4 中几乎所有以 Compat 结尾的类都可以让您免于到处编写 if (Build.VERSION.SDK_INT >= X) 检查。而已。例如,而不是

final Drawable d;
if (Build.VERSION.SDK_INT < 21) 
    // Old method, drawables cannot contain theme references.
    d = context.getResources().getDrawable(R.drawable.some_image);
 else 
    // Drawables on API 21 can contain theme attribute references.
    // Context#getDrawable only exists since API 21.
    d = context.getDrawable(R.drawable.some_image);

你可以写

final Drawable d = ContextCompat.getDrawable(context, R.drawable.some_image);

适用于 cmets 中描述的限制,例如

// This line is effectively equivalent to the above.
ResourcesCompat.getDrawable(context.getResources(), R.drawable.some_image, context.getTheme());

实际上并没有在 Lollipop 之前应用主题属性(这在文档中有说明)。但是您不必编写 if 检查并且您的代码不会在旧设备上崩溃,因为您实际上并没有在那里使用新的 API。

AppCompatResources

另一方面,AppCompatResources 实际上会帮助您为旧平台带来新功能(支持向量、颜色状态列表中的主题引用)。

我应该更喜欢哪一个,为什么?

使用AppCompatResources 以获得与 appcompat-v7 库的其余部分一致的结果。你会得到:

getColorStateList 可以解析带有主题属性引用的颜色(如android:alpha="?android:disabledAlpha"), getDrawable 支持所有平台上的膨胀矢量,这些矢量可绘制对象还理解主题属性引用(例如 android:tint="?colorControlNormal"), appcompat-v7 可绘制对象和颜色(如复选标记或单选按钮)将具有由提供的上下文主题定义的正确颜色, 如果上述内容不适用,则无论如何都会退回到ContextCompat

【讨论】:

【参考方案4】:

API 19+ 解决方法

package com.example;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.view.View;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;

/**
 * https://***.com/a/48237058/1046909
 */
public class AppDrawableCompat 

    public static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) 
        try 
            return AppCompatResources.getDrawable(context, resId);
         catch (Resources.NotFoundException e1) 
            try 
                return ContextCompat.getDrawable(context, resId);
             catch (Resources.NotFoundException e2) 
                return VectorDrawableCompat.create(context.getResources(), resId, context.getTheme());
            
        
    

    @Nullable
    public static Drawable findDrawable (@NonNull Context context, @DrawableRes int resId) 
        try 
            return getDrawable(context, resId);
         catch (Resources.NotFoundException e) 
            return null;
        
    

    public static void setViewBackgroundDrawable(@NonNull View view, @NonNull Context context, @DrawableRes int resId) 
        Drawable drawable = findDrawable(context, resId);
        if (drawable != null) 
            view.setBackground(drawable);
        
    

使用示例(MainActivity#onCreate)

ImageView icon = findViewById(R.id.icon);
AppDrawableCompat.setViewBackgroundDrawable(icon, this, R.drawable.bg_icon);

【讨论】:

以上是关于ResourcesCompat.getDrawable() 与 AppCompatResources.getDrawable()的主要内容,如果未能解决你的问题,请参考以下文章