在早期版本的Android系统中使用矢量图标可能会导致崩溃问题

96

我正在使用安卓中的向量可绘制对象,在,以下是我的一些库和工具版本:

  • Android Studio : 2.0
  • Android Gradle插件 : 2.0.0
  • 构建工具 : 23.0.2
  • Android Support库 : 23.3.0

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

android {  
  defaultConfig {  
    vectorDrawables.useSupportLibrary = true  
   }  
}

值得一提的是,我使用额外的drawable,例如LayerDrawable(layer_list),如Android官方博客所述,在 app:srcCompat 之外为矢量绘图设置绘制。(链接)
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/search"/>
</level-list>

直接引用矢量图形在Lollipop以前的版本中会失败,但是,AppCompat支持在另一个可绘制容器中引用矢量图形,例如StateListDrawable、InsetDrawable、LayerDrawable、LevelListDrawable和RotateDrawable。通过使用这种“间接方式”,您可以在诸如TextView的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矢量兼容性的重复问题。 - AdamHurwitz
请查看 此答案 以了解如何使用VectorDrawable和drawableLeft、drawableRight、drawableTop、drawableBottom。 - Behzad Bahmanyar
你能帮我检查一下这里的答案吗?https://dev59.com/klsW5IYBdhLWcg3w8q_2#40523623 - Yazon2006
在我的情况下,矢量图并不在正确的包中(打开项目视图)。 - murt
17个回答

112

最新更新 - 2019年6月

自原回答以来,支持库已经有了一些改变。现在,即使是Gradle的Android插件也能在构建时自动生成PNG。因此,以下是两种新方法,这些方法现在应该有效。您可以在此处找到更多信息。

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。

与“自动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(或right、top、bottom)代替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支持库23.4.0中重新启用了VectorDrawable。

对于AppCompat用户,我们添加了一个选择加入的API来从资源中重新启用支持矢量图形(与23.2中的行为相同),通过AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) - 请记住,这仍然可能导致内存使用问题和更新配置实例的问题,因此默认情况下它是禁用的。

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

现在,要启用它,您必须执行以下操作:

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

    ...
}
原始回答-2016年4月

我认为这是由于最新库版本23.3.0中禁用了支持向量。

根据这个文章:

对于 AppCompat 用户,由于在版本 23.2.0/23.2.1 中发现的实现问题,我们决定删除使用资源中矢量可绘制对象在早期 Lollipop 设备上的功能 (ISSUE 205236)。 使用 app:srcCompat 和 setImageResource() 仍然有效。

如果您访问问题ISSUE 205236 ,似乎他们将在未来启用它,但内存问题不会很快被修复:

在下一个版本中,我添加了一项选择性 API,您可以重新启用已删除的 VectorDrawable 支持。尽管如此,它具有与以前相同的注意事项(内存使用和配置更新问题)。

我也遇到了类似的问题。因此,在我的情况下,我把所有使用资源中向量可绘制图标恢复为 PNG 图像(因为即使他们提供重新启用它的选项,内存问题仍将持续发生)。

我不确定这是否是最佳选择,但在我看来,它可以解决所有崩溃问题。


1
谢谢@Guilherme P.,但是为什么你没有删除vectorDrawables.useSupportLibrary = true属性,以便重新启用在构建时生成PNG图像呢? - Behzad Bahmanyar
1
由于内存问题,最新版本v23.3.0中禁用了该功能。这样一来,他们就无法在运行时生成png文件了……这也是为什么在日志错误中打印出来的是:未知标签vector(或类似的内容)。 - guipivoto
4
以下是选择加入的说明https://plus.google.com/+AndroidDevelopers/posts/B7QhFkWZ6YX。很遗憾,矢量图在老版本的设备上仍然无法使用。我使用的是Support Library 23.4.0,并且在defaultConfig{}中调用了'generatedDensities = []'和'vectorDrawables.useSupportLibrary = true'。 - AdamHurwitz
1
@AdamHurwitz 我更新了答案...看起来它又被启用了...不过,现在你必须以不同的方式启用它。 - guipivoto
1
@juztcode 你是对的。你应该使用 'androidx.appcompat:appcompat:1.1.0'。 - guipivoto
显示剩余14条评论

65

我也遇到了同样的问题。 但是经过大量的研究和开发,我找到了答案。

对于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" 获取可绘制对象。


60

为了详细说明其他非常好的答案,这里有一张图表可以帮助你。如果你拥有23.4.0至少到25.1.0的支持库,它是有效的。

VectorDrawable cheatsheet


1
如果您正在尝试设置drawableLeft,请按照此处提到的方法将其包装在drawable中。https://medium.com/@chrisbanes/appcompat-v23-2-age-of-the-vectors-91cbafa87c88 - nizam.sp
谢谢,这张图对我们很有帮助。 - silentsudo

39

Guillherme P的答案非常棒。只需进行微小改进即可,如果您在Application类中添加了一次它将同样起作用,您不需要在每个活动中添加那一行。

public class App extends Application {

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

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

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

此外,请确保使用的支持库版本大于v23.4,因为 Google 在这个版本中恢复了对 VectorDrawables 的 Drawable Containers 支持(发布说明)。

更新

并且对于代码更改:

  1. 请确保在每个接受android:src属性的地方更新为app:srcCompat(如果像<bitmap>标记一样无效,IDE会警告您)。
  2. 对于在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。 - Benny
2
是的,我知道。但是需要一个包装器才能使矢量可绘制对象作为TexViews的可绘制对象工作,因此这个答案是不完整的。 - cesards
1
很棒的答案。特别适用于所有活动都扩展自定制基础活动的应用程序。 - Hong

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

解决了它。
vectorDrawables.useSupportLibrary = true

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

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

1
这对我有用。谢谢。我只是删除了 vectorDrawables.useSupportLibrary = true - Android Mediocre
我认为这不是正确的做法,但无论如何我给了你的答案点赞!! - Harish Reddy
谢谢。它适用于我的应用程序。即使已删除,所有矢量可绘制对象仍然有效。该应用程序使用com.android.support:appcompat-v7:28.0.0。 - Hong

11

在 Android 5.0 及以下的版本中,矢量图标无需使用任何兼容性库即可正常工作。

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方法。


如何在选择器中设置矢量图像? - Tushar Gogna
在gradle默认设置中设置vectorDrawables.useSupportLibrary = true和在activity的onCreate方法中设置AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);后,为了避免与TextView中的android:drawableleft冲突,需要通过编程方式将drawable left设置到TextView中,例如:textview.setCompoundDrawablesWithIntrinsicBounds(R.drawable.movie, 0, 0, 0); - Afjalur Rahman Rana

9

经过大量的研发,我们终于解决了在早期版本的设备上出现崩溃的问题。

对于ImageView:

  • 请使用app:srcCompat代替android:src

对于TextView/EditText:

  • 删除drawableleftdrawableright等,并从drawable java代码中设置。

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

对于Build.gradle:

vectorDrawables.useSupportLibrary = true


1
这是唯一在TextInputEditText上有效的解决方案。 - heronsanches
1
太棒了,这解决了我的问题。这应该被接受为答案。 - DJtiwari

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 如果是英文,基本上是同一件事。 - Hossam Hassan

5

对于任何升级到 Android Gradle 3.0 及以上版本的人,无需使用 AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) 或设置 vectorDrawables.useSupportLibrary = true(添加此内容会引起问题),只需使用 app:srcCompat 即可。

我花了两天时间才找出这个问题,并且在谷歌文档中没有找到任何相关文档...


有趣的是,我正在使用gradle 3.3.0,这个解决方案有效。然而,Android Studio仍然告诉我要启用set vectorDrawables.useSupportLibrary = true。 - masterwok

3

使用以下代码之后:

android {
  defaultConfig {
  vectorDrawables.useSupportLibrary = true  
                }
        }




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

尽管如此,以下属性仍存在矢量图像问题:

DrawableEnd, DrawableStart, DrawableTop, DrawableBottom, Background

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

示例:

ic_warranty_icon.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="17dp"
android:height="24dp"
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/布局。

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableStart="@drawable/ic_warranty_icon"
       />


<LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/ic_warranty_icon"
       />

谢谢!对于背景属性,将选择器放在矢量图形之间而不是直接使用矢量图形可在Android 5.0以下的API(19)上起作用。 - Ankur
在我的情况下,即使使用“selector”进行包装,这对API(16)仍然无效。 - mochadwi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接