如何在不缩放的情况下将矢量图居中在图层列表中

121

我试图在LayerList中使用一个VectorDrawable,但不对矢量图进行缩放。比如:

<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
    <item android:gravity="center" android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>

可绘制对象 ic_check_white_48dp 的定义如下:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#FFFFFFFF"
        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

期望的效果是将勾号图标居中于图层可绘制对象中,不进行缩放。问题在于上面的图层列表会导致勾号图标被缩放以适应图层大小。
如果我使用每个密度的PNG替换矢量可绘制对象并修改图层列表,则可以实现期望的效果。
<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
    <item>
        <bitmap android:gravity="center" android:src="@drawable/ic_check_white_48dp"/>
    </item>
</layer-list>

我能用VectorDrawable这个方式来实现吗?


12
似乎这可能只是 API 21/22 中的一个 bug。我刚在一个 API 23 的设备上进行了测试,矢量图标正确地居中显示了。 - ashughes
1
你应该回答自己的问题并接受它。这样更容易被看到,而且问题不会显示为未回答。 - Marcin Koziński
1
我还没有回答我的问题,因为我还没有找到API 21/22上该怎么做的答案。我的临时解决方案是使用PNG而不是矢量图。但仍希望能找到让矢量图正常工作的方法。 - ashughes
1
我在这里打开了一个问题:https://code.google.com/p/android/issues/detail?id=219600 - Etienne Lawlor
这个问题在API 23中已经被修复了,所以我猜测你的错误报告会被标识为已解决。 - ashughes
4个回答

38

我遇到了同样的问题,尝试在分层列表中居中矢量图形。

我有一个解决方法,虽然不完全相同但它能够起作用。您需要为整个可绘制区域设置尺寸,并为矢量项添加填充:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <size android:height="120dp" android:width="120dp"/>
            <solid android:color="@color/grid_item_activated"/>
        </shape>
    </item>
    <item android:top="24dp"
          android:bottom="24dp"
          android:left="24dp"
          android:right="24dp"
          android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>
上面的形状大小设置了整个可绘制对象的大小,在这个例子中为120dp,第二个项目上的填充(padding)在这个例子中为24dp,居中了矢量图像。这不同于使用gravity="center",但这是在API 21和22中使用矢量图像的工作方式。

5
听起来需要知道可绘制对象的确切大小(以设置形状大小),而不是让它填充设置为其中心带有勾选可绘制对象的整个空间。 - ashughes
1
虽然你可能不知道大小,但是你不能使用这种方法更改填充。你可以在ImageView中将可绘制对象层叠在一起。正如我上面所说,在API 21和22中没有完美的解决方案来实现gravity="center" - string.Empty
当与windowBackground一起使用时,它不起作用。在这种情况下,显式大小无效。如果我将图层列表放在另一个图层列表中,它也不起作用。 - Vsevolod Ganin

26

我曾经也遇到过同样的问题。我找到了唯一的解决方法,避免图片变得模糊并扩大了,那就是通过设置图片大小和使用重力来对齐它:

<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
     <item
        android:gravity="center"
        android:width="48dp"
        android:height="48dp"
        android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>

希望这有所帮助!


38
在API 21-22上,这并没有帮助,因为这些API级别上不存在width属性。在API 23上,这些都不再需要了,因为该问题已经得到修复,可拉伸的图片不再被拉伸。 - WonderCsabo
3
对于API 27,这个功能不能正常工作,图像被拉伸到全屏。 - Konstantin Konopko

18

编辑后的解决方案可使您的启动屏在包括API 21到API 23在内的所有API上看起来很棒

首先阅读文章,并按照良好的方式制作启动屏幕。

如果您的标志失真或不适合,并且只针对API 24+,您可以直接在其xml文件中缩小矢量可绘制对象的比例,如下所示:

<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="640"
android:viewportHeight="640"
android:width="240dp"
android:height="240dp">
<path
    android:pathData="M320.96 55.9L477.14 345L161.67 345L320.96 55.9Z"
    android:strokeColor="#292929"
    android:strokeWidth="24" />
</vector>

上面的代码中,我将画在640x640画布上的可绘制对象重新缩放为240x240。然后我只需像这样将其放在我的闪屏可绘制对象中,它就可以正常工作:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque"
android:paddingBottom="20dp" android:paddingRight="20dp" android:paddingLeft="20dp" android:paddingTop="20dp">

<!-- The background color, preferably the same as your normal theme -->
<item>
    <shape>
        <size android:height="120dp" android:width="120dp"/>
        <solid android:color="@android:color/white"/>
    </shape>
</item>

<!-- Your product logo - 144dp color version of your app icon -->
<item
    android:drawable="@drawable/logo_vect"
    android:gravity="center">

</item>
</layer-list>

我的代码实际上只画出了底部图片中的三角形,但你可以看到你可以通过这样做达到什么效果。分辨率最终非常好,与使用位图时获得的像素化边缘截然不同。所以务必使用矢量可绘制对象(有一个名为Vectr的网站,我用它来创建我的项目,而无需下载专门的软件)。

编辑为使其在API21-22-23上也能正常工作

虽然上面的解决方案适用于运行API24+的设备,但当我在运行API22的设备上安装我的应用后,我感到非常失望。我注意到闪屏屏幕又再次尝试填充整个视图,看起来很糟糕。经过半天的纠结后,我最终通过意志力的努力找到了一个解决方案。

您需要创建第二个文件,其名称与闪屏屏幕xml完全相同(比如说splash_screen.xml),并将其放置在两个名为drawable-v22和drawable-v21的文件夹中,这些文件夹都在res/文件夹中(要查看它们,您需要将项目视图从Android更改为Project)。这用于告诉您的手机在相关设备运行与drawable文件夹中-vXX后缀对应的API时,重定向到放置在这些文件夹中的文件,请参阅此链接。将以下代码放入您在这些文件夹中创建的splash_screen.xml文件的Layer-list中:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:opacity="opaque">

<!-- The background color, preferably the same as your normal theme -->
<item
    android:gravity="center">
    <shape android:shape="rectangle">
        <solid android:color="@android:color/white"/>

    </shape>
</item>

<!-- Your product logo - 144dp color version of your app icon -->
<item
    android:gravity="center">
    <bitmap
        android:gravity="center"
        android:src="@drawable/logo_vect"/>

</item>
</layer-list>

这就是文件夹的外观

由于某些 API 的原因,您必须将可绘制对象包装在位图中,以使其正常工作,但最终结果看起来相同。问题在于,您必须使用附加的 drawable 文件夹来处理,因为第二个版本的 splash_screen.xml 文件会导致在运行 API 高于 23 的设备上根本不显示闪屏屏幕。您可能还需要将 splash_screen.xml 的第一个版本放入 drawable-v24 中,因为 Android 会默认使用它可以找到的最接近的 drawable-vXX 文件夹。

我的启动画面


3
使用<bitmap android:src="logo_vect"/>似乎表明在API级别21和22上可以将矢量可绘制对象用作位图的源。但根据我的经验,这总是导致运行时崩溃: java.lang.RuntimeException: Unable to start activity android.content.res.Resources$NotFoundException: File res/drawable-v21/splash.xml from drawable resource ID #0x7f080305 Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #X: <bitmap> requires a valid src attribute - Erik
从我所了解的情况来看,@Erik,你的异常信息表明你的logo_vect文件可能不存在或格式不正确。你有检查过吗? - quealegriamasalegre
你尝试过使用上面的三角形路径吗? - quealegriamasalegre
是的,不成功。而且是的。 - Erik
嗨,我只是复制并粘贴了整个包含位图的XML代码,因为它对我有效。所以这种方式也应该适用于您。您在API 21设备上运行应用程序时是否遇到错误?如果没有,您可能需要创建drawable-24文件夹,并将不带位图的splash_screen xml放入其中(即出现在我的答案中的第一个xml)。不确定我们有什么不同之处。 - quealegriamasalegre
显示剩余5条评论

10

我目前正在制作一个带有矢量图形的启动画面,使其水平和垂直居中。这是我在 API 级别 25 上得到的效果:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">

<!-- background color, same as theme -->
<item android:drawable="@android:color/white"/>

<!-- app logo -->

<item
    android:drawable="@drawable/my_app_logo"
    android:gravity="center_horizontal|center_vertical"
    android:width="200dp"
    android:height="200dp"
    />
</layer-list>

如果您想调整矢量图的尺寸,请修改上面的android:widthandroid:height属性,而不必修改原始的可绘制对象。

此外,我的经验是不要将矢量图放在位图标签中,因为这对我从来没有起作用过。位图标签适用于.png、.jpg和.gif格式文件,而不适用于矢量图。

参考资料

https://developer.android.com/guide/topics/resources/drawable-resource#LayerList

https://developer.android.com/guide/topics/resources/more-resources.html#Dimension


3
关于向量图形需要位于<item>标签而不是<bitmap>标签中的内容,你指出了一个好问题。 - 0xMatthewGroves
在我的情况下,它也适用于 PNG 图像,但正如你所说的,位图标签不起作用,非常感谢 :) - nasibeyyubov

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