在旧版本的Android上图层列表中的矢量可绘制物品

34

在较新的 Android 版本中,以下代码:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
            <shape android:shape="oval">
                <solid android:color="#bdbdbd" />
                <size
                    android:width="60dp"
                    android:height="60dp" />
            </shape>
    </item>
    <item
        android:drawable="@drawable/ic_library_books_black_24dp"
        android:gravity="center"
        android:width="40dp"
        android:height="40dp"
        >
    </item>
</layer-list>

完美地生成了这个:

显示灰色圆形背景上的白名单的向量可绘制对象

然而,早期的Android版本(从我测试的结果来看,包括API 16和19)并不喜欢这样做,会导致以下问题:

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: package.app, PID: 11490
              android.view.InflateException: Binary XML file line #26: Error inflating class ImageView

通货膨胀问题。我已经在所有的ImageView中使用了app:srcCompat,所以那里没有问题。

标准矢量图形也可以正常工作,但当放置在layer-list中时,它们会引起混乱。是否有任何解决方法?

4个回答

34

您在layer-list中的矢量图可缩放图形的宽度/高度属性仅支持API 23(Marshmallow)及更高版本。如果您在Android Studio编辑器中查看图层列表可缩放图形,则这些属性周围应该有黄色块,并提示警告,指出此功能在旧设备上无法可靠地工作。

但我认为您可以通过以下方法消除警告并实现相同的居中效果:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="oval">
            <solid android:color="#bdbdbd" />
            <size
                android:width="60dp"
                android:height="60dp" />
        </shape>
    </item>
    <item
        android:drawable="@drawable/ic_library_books_black_24dp"
        android:top="10dp"
        android:bottom="10dp"
        android:left="10dp"
        android:right="10dp">
    </item>
</layer-list>

我在一部旧的API 15手机上测试过,它可以正常工作。我希望这对您也有效。

更新:

在此答案的以前版本中,我建议不要在图层列表中使用vectorDrawables.useSupportLibrary = true,因为它会导致崩溃。但是,我最近学到了一种解决方法,似乎可以修复崩溃(同时避免@android developer所正确提到的大型自动生成的PNG文件)。以下是需要执行的操作摘要:

确保在您的xml中使用srcCompat。

    <android.support.v7.widget.AppCompatImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:srcCompat="@drawable/layer_list_with_svg" />

在 app/build.gradle 中添加“vectorDrawables.useSupportLibrary”

 android {
   defaultConfig {
     vectorDrawables.useSupportLibrary = true
   }
 }

在onCreate中添加'setCompatVectorFromResourcesEnabled(true)'

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    setContentView(R.layout.activity_main);
}

为什么所有这些都是必要的?

根据一位更有经验的人向我解释,这与Android如何加载兼容矢量图形有关。如果您使用 vectorDrawables.useSupportLibrary = true ,那么当您使用 app:srcCompat 时,图像视图将加载 VectorDrawableCompat 可兼容的可绘制对象。在填充层列表可绘制对象时,无法找到创建这些引用的矢量图形的方法。但是如果您打开 setCompatVectorFromResourcesEnabled,它将试图在比图像视图及其 app:srcCompat属性更低的级别上挂钩矢量图形加载,因此能够找出在图层列表中引用的矢量图形的加载方法。


1
我认为你的项目不只是使用 VectorDrawable 本身。我认为它还使用了自动生成的 API 15 文件。 - android developer
1
它在API 19级别上绝对可以工作。但请查看更新的答案以获取更多信息。 - Rapunzel Van Winkle
4
应用程序崩溃,直到我在 Application 的 onCreate 方法中添加了 AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); 才解决了问题,而不是在 Activity 中添加。因为我在主题中使用了 layer-list 作为闪屏画面。 - Artem_Iens

9
这个答案参考了Chris Banes(他在Support Library上工作)的文章AppCompat - Age of the Vectors。对于这个问题,我们特别关注标题为“神奇”的方法的部分。
你遇到的崩溃是因为支持库默认只允许某些使用VectorDrawables的方式,而layer-list不是其中之一。
有一个特定的代码块可以添加到你的Activity顶部,以启用其他VectorDrawable的使用,例如<layer-list>
static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
注意:链接的文章中有一个错别字在方法名称中,使用“FromSources”,应该是如上所示的“FromResources”。

您需要将此添加到每个要使用此类可绘制对象的活动中,或者将其包含在其他活动扩展的BaseActivity类中。

根据文章,在以下情况下应该可以工作:

DrawableContainers引用其他仅包含矢量资源的绘图资源的绘图资源。

...StateListDrawable...InsetDrawable、LayerDrawable、LevelListDrawable和RotateDrawable。

应该注意,这种方法非常谨慎地使用了“可能”一词,这可能会起作用,并且默认情况下未启用,因此请注意并检查它是否确实适合您!

现在这个问题实际上有另一个维度,感谢其他用户Selim Ajimi和Rapunzel Van Winkle在他们的答案中解决了这个问题。在API中,的行为有所不同,特别是您的的宽度和高度属性只支持API 23+。这不是导致崩溃的原因,也不会导致应用程序崩溃,但这意味着一旦您将其在早期的API中运行,您的图像将无法按预期显示。
Rapunzel Van Winkle的建议确实似乎是正确地跨API正确定位可绘制对象的好方法(已在API 16和24上测试)。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="oval">
            <solid android:color="#bdbdbd" />
            <size
                android:width="60dp"
                android:height="60dp" />
        </shape>
    </item>
    <item
        android:drawable="@drawable/ic_library_books_black_24dp"
        android:top="10dp"
        android:bottom="10dp"
        android:left="10dp"
        android:right="10dp"
        >
    </item>
</layer-list>

1
非常感谢!它在KitKat的图层列表中起作用了。 - charlag
2
请注意,由于AppCompat中的一个错误,直接在item元素上设置gravity属性会导致在至少Pre-lollipop API上拉伸可绘制对象。在该问题修复之前,如果需要设置gravity,您只能使用png/bitmap并在bitmap元素上设置gravity。 - Javad
1
根据文档,不建议使用setCompatVectorFromResourcesEnabled功能:“此功能默认为禁用状态,因为启用它可能会导致内存使用问题和更新配置实例的问题。如果您手动更新配置,则可能不想启用此功能。你已经被警告了。”。链接:https://developer.android.com/reference/android/support/v7/app/AppCompatDelegate.html#setCompatVectorFromResourcesEnabled(boolean)。我在我的答案中提供了替代方案。 - android developer

1

对于旧版本的Android,支持VectorDrawble是相当有限的,假设您使用vectorDrawables.useSupportLibrary = true

您可以在API级别7及更高版本上使用VectorDrawableCompat,以及在运行Android 5.0(API级别11)及更高版本的所有设备上使用AnimatedVectorDrawableCompat。由于Android加载可绘制对象的方式,并非每个接受可绘制对象ID的地方(例如XML文件)都支持加载矢量可绘制对象。android.support.v7.appcompat包添加了许多功能,使得轻松使用矢量可绘制对象成为可能。首先,当您将android.support.v7.appcompat包与ImageView或其子类(如ImageButton和FloatingActionButton)一起使用时,您可以使用新的app:srcCompat属性引用矢量可绘制对象,以及任何其他可用于android:src的可绘制对象。

即使在代码中,您也受到限制:

要在运行时更改可绘制对象,您可以像以前一样使用setImageResource()方法。使用AppCompat和app:srcCompat是将矢量可绘制对象集成到应用程序中最可靠的方法。

你可以做什么?

几个可能的解决方案:

  1. 为你在LayerDrawable中使用的VectorDrawable准备另一种可替代的drawable。将其放置在res/drawable-xxhdpi文件夹中,将VectorDrawable放入res/drawable-anydpi文件夹中。

  2. 在应用程序具有能够使用VectorDrawable的minSdk之前,不要使用vectorDrawables.useSupportLibrary = true。这将有效,因为IDE将为您生成PNG文件。

  3. 以编程方式创建layerList(示例here),并为了放置VectorDrawable,使用AppCompatResources.getDrawable。您也可能能够使用XML来完成它,除了放置VectorDrawable的部分。

顺便提一下,它被限制的原因是谷歌未能使其高效且没有问题。您可以通过使用AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)来访问它们最初尝试的完全支持,但正如文档所说,这样做存在风险

此功能默认为禁用状态,因为启用它可能会导致内存使用问题,并且更新配置实例时出现问题。如果您手动更新配置,则可能不想启用此功能。您已经被警告了


嘿!你提到了在运行时更改可绘制对象! 对于API> = 16,如何实现这一点?我有相同的XML文件,它可以很好地工作,但我无法在运行时更改图层可绘制对象! - Vasco
你能参考一下这个链接吗? - Vasco
@VasilVasilev,你需要使用AppCompatResources.getDrawable(...)来使用VectorDrawable。同时,请确保你的VectorDrawable文件在res/drawable-nodpi中而不是其他任何地方(与向导所做的相反)。别忘了启用vectorDrawables.useSupportLibrary=true标志。 - android developer

1
<?xml version="1.0" encoding="utf-8"?>
<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:drawable="@[package:]drawable/drawable_resource"
        android:id="@[+][package:]id/resource_name"
        android:top="dimension"
        android:right="dimension"
        android:bottom="dimension"
        android:left="dimension" />
</layer-list>

正如您所见这里没有宽度/高度属性...

我认为将位图附加到项目上是最好的解决方案。


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