在 Lollipop 崩溃前使用 android vector Drawables

Posted

技术标签:

【中文标题】在 Lollipop 崩溃前使用 android vector Drawables【英文标题】:Using android vector Drawables on pre Lollipop crash 【发布时间】:2016-08-20 10:07:08 【问题描述】:

我在 Lollipop 之前的 Android 中使用矢量可绘制对象,这些是我的一些库和工具版本:

android Studio : 2.0 Android Gradle 插件:2.0.0 构建工具:23.0.2 Android 支持库:23.3.0

我在我的应用级别添加了这个属性Build.Gradle

android   
  defaultConfig   
    vectorDrawables.useSupportLibrary = true  
     

还值得一提的是,我使用了一个额外的可绘制对象,例如 Android 官方博客 (link here) 中所述的 LayerDrawable(layer_list),用于为 app:srcCompat 之外的矢量可绘制对象设置可绘制对象

<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/search"/>
</level-list>

您会发现在外部直接引用矢量可绘制对象 app:srcCompat 将在 Lollipop 之前失败。但是,AppCompat 确实 支持在另一个引用时加载矢量可绘制对象 可绘制容器,例如 StateListDrawable、InsetDrawable、 LayerDrawable、LevelListDrawable 和 RotateDrawable。通过使用这个 indirection,可以在TextView等情况下使用vector drawables android:drawableLeft 属性,通常不能 支持矢量绘图。

当我使用app:srcCompat 时一切正常,但是当我使用时:

android:background
android:drawableLeft
android:drawableRight
android:drawableTop
android:drawableBottom

在 Lollipop 之前的 ImageViewImageButtonTextViewEditText 上,它引发了一个期望:

Caused by: android.content.res.Resources$NotFoundException: File res/drawable/search_toggle.xml from drawable resource ID #0x7f0200a9

【问题讨论】:

Android vector compatibility的可能重复 要了解如何将 VectorDrawable 与 drawableLeft、drawableRight、drawableTop、drawableBottom 一起使用,请查看This answer 你能在这里查看我的答案吗? ***.com/a/40523623/2557258 在我的情况下,vactor drawables 不在正确的包中(打开项目视图)这里的原始答案***.com/a/35836318/2163045 【参考方案1】:

最新更新 - 2019 年 6 月

支持库自原始答案以来发生了一些变化。现在,即使是 Gradle 的 Android 插件也能够在构建时自动生成 PNG。因此,以下是目前应该可以使用的两种新方法。你可以找到更多信息here:

PNG 生成

Gradle 可以在构建时自动从您的资产创建 PNG 图像。但是,在这种方法中,并非所有 xml 元素都受支持。这个解决方案很方便,因为您不需要更改代码或 build.gradle 中的任何内容。只需确保您使用的是 Android Plugin 1.5.0 或更高版本Android Studio 2.2 或更高版本

我在我的应用程序中使用了这个解决方案并且工作正常。不需要额外的 build.gradle 标志。没有黑客是必要的。如果你去 /build/generated/res/pngs/... 你可以看到所有生成的 PNG。

所以,如果你有一些简单的图标(因为不是所有的 xml 元素都受支持),这个解决方案可能对你有用。只需更新您的 Android Studio 和 Gradle 的 Android 插件即可。

支持库

这可能是适合您的解决方案。如果您来到这里,则意味着您的 Android Studio 不会自动生成 PNG。所以,你的应用程序崩溃了。

或者,您可能根本不希望 Android Studio 生成任何 PNG。

与支持 XML 元素子集的“自动 PNG 生成”不同,此解决方案支持所有 xml 标记。所以,你完全支持你的矢量绘图。

您必须首先更新您的 build.gradle 以支持它:

android 
  defaultConfig 
    // This flag will also prevents Android Studio from generating PNGs automatically
    vectorDrawables.useSupportLibrary = true
  


dependencies 
  // Use this for Support Library
  implementation 'com.android.support:appcompat-v7:23.2.0' // OR HIGHER

  // Use this for AndroidX
  implementation 'androidx.appcompat:appcompat:1.1.0' // OR HIGHER

然后,在加载VectorDrawables 时使用app:srcCompat 而不是android:src。不要忘记这一点。

对于TextView,如果您使用的是支持库的androidx 版本,则可以使用app:drawableLeftCompat(或右、上、下)代替app:drawableLeft

如果是CheckBox/RadioButton,请使用app:buttonCompat 而不是android:button

如果您没有使用支持库的androidx 版本并且您的minSdkVersion17 或更高版本或使用按钮,您可以尝试通过编程方式设置

Drawable icon = AppCompatResources.getDrawable(context, <drawable_id>);
textView.setCompoundDrawablesWithIntrinsicBounds(<leftIcon>,<topIcon>,<rightIcon>,<bottomIcon>);

更新 - 2016 年 7 月

他们在Android Support Library 23.4.0

中重新启用了 VectorDrawable

对于 AppCompat 用户,我们添加了一个 opt-in API,以通过 AppCompatDelegate.setCompatVectorFromResourcesEnabled 从资源中重新启用对 Vector Drawable 的支持(在 23.2 中发现的行为) (true) - 请记住,这仍然会导致内存使用问题和更新配置实例的问题,因此默认情况下它被禁用。

也许build.gradle 设置现在已过时,您只需要在适当的活动中启用它(但是,需要测试)。

现在,要启用它,您必须:

public class MainActivity extends AppCompatActivity 
    static 
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    

    ...


原始答案 - 2016 年 4 月

我认为这是因为支持向量在最新的库版本中被禁用:23.3.0

据此POST:

对于 AppCompat 用户,由于版本 23.2.0/23.2.1 (ISSUE 205236) 中的实现中发现的问题,我们决定删除允许您从棒棒糖之前设备上的资源中使用矢量可绘制对象的功能。使用 app:srcCompat 和 setImageResource() 继续有效。

如果你访问 issueISSUE 205236,他们似乎会在未来启用,但内存问题不会很快修复:

在下一个版本中,我添加了一个可选 API,您可以在其中重新启用已删除的 VectorDrawable 支持。虽然它带有与以前相同的警告(内存使用和配置更新问题)。

我遇到了类似的问题。因此,就我而言,我将所有使用可绘制矢量的图标从资源中再次恢复为 PNG 图像(因为即使在他们提供再次启用它的选项后,内存问题仍会继续发生)。

我不确定这是否是最佳选择,但在我看来,它修复了所有崩溃问题。

【讨论】:

感谢@Guilherme P。但是您为什么不删除vectorDrawables.useSupportLibrary = true 权限以重新启用在构建时生成png 由于内存问题,在最新版本 v23.3.0 中已禁用。这样,他们就无法在运行时生成 png ......这就是他们在 logcat 错误中打印的原因:未知标签向量(或类似的东西)。 这里是选择加入说明plus.google.com/+AndroidDevelopers/posts/B7QhFkWZ6YX。不幸的是,VectorDrawables 仍然无法在 Pre-Lollipop 设备上运行。我在支持库 23.4.0 上,并且在 defaultConfig 中调用了“generatedDensities = []”和“vectorDrawables.useSupportLibrary = true”。 @AdamHurwitz 我更新了答案.. 好像又启用了.. 不过,你现在必须以不同的方式启用它。 @juztcode 你是对的。你应该使用'androidx.appcompat:appcompat:1.1.0'【参考方案2】:

我遇到了同样的问题。 但是做了很多研发我得到了答案。

对于 Imageview 和 ImageButton 使用,app:srcCompat="@drawable/...." 对于其他视图,如 Button、Textview,而不是在 XML 中使用 "drawableLeft/right...",以编程方式将可绘制对象指定为:

button.setCompoundDrawablesWithIntrinsicBounds(AppCompatResources.getDrawable(mContext,R.drawable.ic_share_brown_18dp), null, null, null);

并使用“AppCompatResources”来获取drawable。

【讨论】:

【参考方案3】:

要详细说明其他very good answers,这里是a diagram,可以帮助您。如果您的支持库从 23.4.0 到至少 25.1.0,则有效。

【讨论】:

如果您尝试设置 drawableLeft,请将其包装在一个可绘制对象中,如此处所述。 medium.com/@chrisbanes/… 感谢这张图表对我们帮助很大。【参考方案4】:

Guillherme P 的回答非常棒。只是为了做一个小的改进,您不需要在每个活动中添加该行,如果您在 Application 类中添加一次它也可以工作。

public class App extends Application 

static 
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

记住:你仍然需要在 gradle 中启用支持库:

android 
  defaultConfig 
    vectorDrawables.useSupportLibrary = true
  

此外,请确保您使用的支持库版本高于 v23.4,当 Google 重新添加对 VectorDrawables 的 Drawable Containers 的支持时 (release note)

更新

对于代码更改:

    确保将每个接受android:src 属性的位置更新为app:srcCompat(如果它像&lt;bitmap&gt; 标记一样无效,IDE 会警告您)。

    对于TextView 和类似视图中使用的drawableLeftdrawableStartdrawableRightdrawableEnd 属性和类似视图,您现在必须以编程方式设置它们。设置drawableStart的示例:

    Drawable drawable = AppCompatResources.getDrawable(
            getContext(),
            R.drawable.your_vector_drawable);
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) 
        textView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, null, null);
    
    

【讨论】:

请记住,您需要在 gradle 中启用矢量可绘制对象的支持库:vectorDrawables.useSupportLibrary = true 是的。我知道。但是需要一个包装器来使矢量绘图作为 TexViews 的绘图工作,所以这个答案是不完整的。 很好的答案。它特别适用于所有 Activity 都从自定义基础 Activity 扩展而来的应用。【参考方案5】:

我遇到了同样的问题。并通过删除

来修复它
vectorDrawables.useSupportLibrary = true

我的目标版本是25,支持库是

 compile 'com.android.support:appcompat-v7:25.3.1'

【讨论】:

这对我有用。谢谢。我刚刚删除了vectorDrawables.useSupportLibrary = true 我认为这不是正确的做法,无论哪种方式,我都对您的回答投了赞成票。!! 谢谢。 t 适用于我的应用程序。如果已将其删除,所有矢量可绘制对象仍然有效。该应用使用 com.android.support:appcompat-v7:28.0.0【参考方案6】:

pre-lollipop 上的 VectorDrawables 应该可以在不使用的情况下正常工作

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

如果你想在 ImageViews 中使用 VectorDrawables,你可以使用属性srcCompat 并且它会工作,但在 Buttons 或 TextViews 中它不会,所以你需要将 Drawable 包装到一个 InsetDrawable 或一个 LayerDrawable。我发现了另一个技巧,如果你使用数据绑定,你可以这样做:

android:drawableLeft="@@drawable/vector_ic_access_time_24px"
android:drawableStart="@@drawable/vector_ic_access_time_24px"

这会神奇地起作用,我还没有调查幕后发生的事情,但我猜 TextView 正在使用 AppCompatResources 或类似的 getDrawable 方法。

【讨论】:

如何在选择器中设置矢量图? 在 gradle 默认设置 vectorDrawables.useSupportLibrary = true 并在创建活动中设置 AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); 后避免与 android:drawableleft in Textview,以编程方式将drawble left设置为textview:textview.setCompoundDrawablesWithIntrinsicBounds(R.drawable.movi​​e, 0, 0, 0);【参考方案7】:

大量的研发,终于得到这个解决棒棒糖前设备崩溃的解决方案。

对于图像视图

使用 app:srcCompat 而不是 android:src

对于 TextView/EditText

删除 drawableleftdrawableright.... 并从可绘制的 java 代码中设置。

txtview.setCompoundDrawablesWithIntrinsicBounds(AppCompatResources.getDrawable(EventDetailSinglePage.this, R.drawable.ic_done_black_24_n), null, null, null);

对于 Build.gradle

vectorDrawables.useSupportLibrary = true

【讨论】:

这是适用于 TextInputEditText 的独特解决方案。 太棒了,这解决了我的问题。这应该被接受。【参考方案8】:

最简单的使用方法:

app:drawableRightCompat ="@drawable/ic_mobilelogin"
app:drawableEndCompat="@drawable/ic_mobilelogin"
app:srcCompat="@drawable/ic_mobile"

并且...只需使用 app:**Compat 以实现兼容性。 同时添加对build.gradle(模块)的支持

android 
   defaultConfig 
       vectorDrawables.useSupportLibrary = true
   

【讨论】:

app:drawableEndCompat 和 app:drawableRightCompat 如果是英文的话,几乎是一回事【参考方案9】:

对于任何升级到android gradle 3.0及以上的人,都不需要使用AppCompatDelegate.setCompatVectorFromResourcesEnabled(true),或者设置vectorDrawables.useSupportLibrary = true(添加这个会导致问题)并使用app:srcCompat,它就可以了。

花了我两天时间弄明白,在google的文档中没有找到任何相关的文档...

【讨论】:

有趣,我使用的是 gradle 3.3.0,这个解决方案有效。但是,Android Studio 仍然告诉我启用 set vectorDrawables.useSupportLibrary = true。【参考方案10】:

使用下面的代码后。

android 
  defaultConfig 
  vectorDrawables.useSupportLibrary = true  
                
        




public class App extends Application 
static 
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

仍然存在以下属性的矢量图像问题

可绘制结束, 可绘制开始, 可绘制顶部, 可绘制底部, 背景

在这种情况下,请按照以下说明,不要直接引用矢量图,而是使用选择器标签作为中间可绘制文件。

示例:

ic_warranty_icon.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:
android:
android:autoMirrored="true"
android:viewportWidth="17"
android:viewportHeight="24">

<path
    android:fillColor="#fff"
    android:pathData="M10.927,15.589l-1.549,0.355a7.47,7.47 0,0 1,-0.878 0.056c-4.136,0 -7.5,-3.364 -7.5,-7.5s3.364,-7.5 7.5,-7.5 7.5,3.364 7.5,7.5c0,3.286 -2.126,6.078 -5.073,7.089zM8.5,2a6.508,6.508 0,0 0,-6.5 6.5c0,3.583 2.917,6.5 6.5,6.5s6.5,-2.917 6.5,-6.5 -2.917,-6.5 -6.5,-6.5z" />

safe_ic_warranty_icon.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_warranty_icon"  />
</selector>

您的文本视图/布局。

<TextView
        android:layout_
        android:layout_
        android:drawableStart="@drawable/ic_warranty_icon"
       />


<LinearLayout
        android:layout_
        android:layout_
        android:background="@drawable/ic_warranty_icon"
       />

【讨论】:

谢谢!!!对于背景属性,在棒棒糖之前的 api (19) 之间添加选择器而不是直接使用矢量可绘制对象。 在我的情况下,这仍然不适用于 API (16),甚至使用 selector【参考方案11】:

我在 Pre-lollipop 设备上使用 VectorDrawables,我就是这样做的:-

第 1 步: 将其放入您的应用级别 gradle 中。

android 
  defaultConfig 
    vectorDrawables.useSupportLibrary = true
  

第 2 步:

把它放在你的应用程序类中,不要忘记在清单文件中注册你的应用程序类。

public class App extends Application 
    static 
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    

第 3 步:

使用获取 VectorDrawables,

imageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.my_vector_drawable));

【讨论】:

【参考方案12】:

我们尝试了 3 件事

vectorDrawables.useSupportLibrary = true

在Application类中设置setCompatVectorFromResourcesEnabled

static 
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

并使用app:srcCompat

但即使在那之后它还是失败了

Resources$NotFoundException: File res/drawable/$my_icon__0.xml from color state list resource ID #0x7f080008

然后我们发现我们的 SVG 有一个 Gradient 标签。 将渐变标签转换为 API = 24 有效。

从这个答案https://***.com/a/47783962/2171513得到帮助

【讨论】:

我不知道为什么没有人支持这个,但这实际上可以解决我的问题。谢谢【参考方案13】:

我为此苦苦挣扎了好几个小时。

我尝试了这些答案告诉我的所有方法,但我的应用程序并没有停止崩溃。我删除了这一行:app:srcCompat="@drawable/keyboard",我的应用程序停止崩溃。然后当我添加同样的东西时,它又开始崩溃了。所以我决定打开那个文件,我在第一行看到一个错误,说

"可绘制的“键盘”在基本可绘制对象中没有声明 文件夹;这可能会导致崩溃。

我右键单击该文件并单击“在资源管理器中显示”,它不在 drawable 文件夹中,而是在 drawable-v24 目录中。所以我把它复制并粘贴到drawable目录,终于摆脱了崩溃。

【讨论】:

【参考方案14】:

只需将可绘制的向量重叠到状态列表即可解决问题

例如,您有返回箭头矢量图像:

ic_back_arrow.xml

是的,您应该将其重叠到层列表 xml (ic_back_arrow_vector_vector.xml):

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_back_arrow"/>
</layer-list>

因为逻辑:

vectorDrawables.useSupportLibrary = true

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

在某些中国设备和较旧的三星设备上不会为您提供帮助。如果不重叠,就会失败。

【讨论】:

【参考方案15】:

就我而言,我使用的是TabLayout,我将其配置为:

TabLayoutMediator(tabLayout!!, viewPager!!)  tab, position ->
    if (position == 0)
        tab.icon = ResourcesCompat.getDrawable(resources, R.drawable.ic_list, theme)
    else
        tab.icon = ResourcesCompat.getDrawable(resources, R.drawable.ic_building_map, theme)
.attach()

应用程序在tab.icon = ... 行崩溃

我将这些更改为tab.setIcon(R.drawable.my_vector_asset),因为它:

TabLayoutMediator(tabLayout!!, viewPager!!)  tab, position ->
    if (position == 0)
        tab.setIcon(R.drawable.ic_list)
    else
        tab.setIcon(R.drawable.ic_building_map)
.attach()

它成功了!

【讨论】:

【参考方案16】:

Guilherme P 的建议对我不起作用。我继续并决定在我需要在 app:srcCompat 之外做事情的地方使用 png:srcCompat 即 drawableLeft、drawableRight 等。这是一个非常容易进行的更改,并且没有潜在的内存问题 AppCompatDelegate.setCompatVectorFromResourcesEnabled(真的); 介绍。

【讨论】:

【参考方案17】:

Benny 的 答案的替代方法是创建一个Activity 超类:

public abstract class VectorDrawableActivity extends AppCompatActivity 
  static 
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
  

  //...

现在扩展 VectorDrawableActivity 而不是 AppCompatActivity

【讨论】:

@cesards 为什么不呢?

以上是关于在 Lollipop 崩溃前使用 android vector Drawables的主要内容,如果未能解决你的问题,请参考以下文章

Android Lollipop 上的键盘隐藏崩溃

Android Lollipop:将应用小部件添加到主屏幕时启动器崩溃

Android 推送通知服务未在 Lollipop 上启动

Android推送通知服务无法在Lollipop上启动

Lollipop 上的 Android Button Ripple 和 pre lollipop 上的高亮显示

Lollipop 设备的共享元素转换崩溃