How to center vector drawable in layer-list without scaling
Asked Answered
A

4

121

I am attempting to use a VectorDrawable in a LayerList without scaling the vector. For example:

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

The drawable ic_check_white_48dp id defined as:

<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>

The desired effect is for the check icon to be centered in the layer drawable, without scaling. The issue is, the layer-list above causes the check icon to be scaled to fit the layer size.

I can produce the desired effect if I replace the vector drawable with PNGs for each density and modify the layer-list as such:

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

Is there any way I can do this using a VectorDrawable?

Audible answered 16/1, 2016 at 19:25 Comment(5)
It appears that this may simply be a bug in API 21/22. I just tested on an API 23 device and the vector drawable was correctly centered.Audible
You should answer your own question and accept it. That way it's more visible and the question doesn't show up as unanswered.Lyudmila
I haven't answered my question because I don't yet have an answer for what to do on API 21/22. My temporary solution was to use PNGs instead of vectors. Hoping to still find a way to make vectors work though.Audible
I opened an issue here : code.google.com/p/android/issues/detail?id=219600Hawking
This is already fixed in API 23, so I'm guessing your bug report will just be marked as fixed.Audible
P
38

I ran into the same problem trying to center vectors drawables on a layered list.

I have a workaround, its not exactly the same but it works, you need to set a size for the entire drawable and add padding to the vector item:

<?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>

The size of the shape above sets the size of the entire drawable, 120dp in this example, the padding on the second item, 24dp in this example, centers the vector image.

Its not the same as using the gravity="center" but its working way of using vectors in API 21 and 22.

Pistole answered 8/7, 2016 at 9:40 Comment(3)
This sounds like it would require you to know the exact size of the drawable (in order to set the shape size), rather than letting it fill the entire space it's set to with the check drawable centered within it.Audible
it is possible to not know the size but you cant change the padding with this method, you could layer the drawables on top of each other in imageviews. As i stated above there is no perfect solution for a gravity="center" in API 21 and 22.Pistole
It doesn't work when using with windowBackground. Explicit sizes do nothing in this case. And it doesn't work either if I put layer list inside another layer list.Brunhilda
H
26

Been struggling with the same problem. The only way I found to fix this and avoid the drawable to scale up was to set the drawable size and using gravity to align it:

<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>

Hope it helps!

Histoplasmosis answered 30/8, 2016 at 16:23 Comment(2)
This does not help on API 21-22, because the width attribute is not available on those API levels. On API 23, these are not needed, because the bug is fixed and the drawable is not stretched anymore.Blarney
This doesn't work correctly for API 27, image is stretched to fullscreenLives
N
18

Edited solution that will make your SplashScreen look great on all APIs including API21 to API23

First of all read this article and follow the GOOD way of making a splash screen.

If your logo is distorted or wont fit and you are only targeting APIs24+ you can simply scale down your vector drawable directly in its xml file like so:

<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>

in the code above I am rescaling a drawable I drew on a 640x640 canvas to be 240x240. then i just put it in my splash screen drawable like so and it works great:

<?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>

my code is actually only drawing the triangle in the picture at the bottom but here you see what you can achieve with this. Resolution is finally great as opposed to the pixelated edges I was getting when using bitmap. so use a vector drawable by all means (there is a site called vectr that I used to create mine without the hasle of downloading specialized software).

EDIT in order to make it work also on API21-22-23

While the solution above works for devices runing API24+ I got really disappointed after installing my app a device running API22. I noticed that the splashscreen was again trying to fill the entire view and looking like shit. After tearing my eyebrows out for half a day I finally brute-forced a solution by sheer willpower.

you need to create a second file named exactly like the splashscreen xml (lets say splash_screen.xml) and place it into 2 folders called drawable-v22 and drawable-v21 that you will create in the res/ folder (in order to see them you have to change your project view from Android to Project). This serves to tell your phone to redirect to files placed in those folders whenever the relevant device runs an API corresponding to the -vXX suffix in the drawable folder, see this link. place the following code in the Layer-list of the splash_screen.xml file that you create in these folders:

<?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>

these is how the folders look

For some reason for these APIs you have to wrap your drawable in a bitmap in order to make it work and jet the final result looks the same. The issue is that you have to use the aproach with the aditional drawable folders as the second version of the splash_screen.xml file will lead to your splash screen not being shown at all on devices running APIs higher than 23. You might also have to place the first version of the splash_screen.xml into drawable-v24 as android defaults to the closest drawable-vXX folder it can find for resources.

my splashscreen

Nazler answered 8/2, 2020 at 5:9 Comment(10)
With <bitmap android:src="logo_vect"/> you seem to suggest that on API levels 21 and 22 it is possible to use a vector drawable as the bitmap's source. In my experience this always leads to a runtime crash: 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 attributeUlceration
@Ulceration from what i gather your exception is saying that your logo_vect file might either not exist or have the wrong format. have you checked?Nazler
have you tried with the triangle path aboveNazler
Yes, I have done exactly as you instructed. 1: Create the vector drawable (I copied your triangle vector). 2: create /res/drawable-v21/splash.xml. 3: Put your layered list with a <bitmap ... /> in there. 4: Use @drawable/splash in the android:windowBackground item of the launcher activity theme. 5: Build and launch my app on API 21. 6: 💥 Crash (like I said above): <bitmap> requires a valid src attribute.Ulceration
weird, have you tried simply using a different vector drawable? and are you able to see the triangle in the android studio previewNazler
Yes, unsuccessfully. And yes.Ulceration
Hi I just copy pasted the entire xml code containing the bitmap as it is working for me. so it should also work for you this way. Are you getting the error when running the app on an API 21 device? if not you might need to create the drawable-24 folder and put the splash_screen xml without the bitmap in there (i.e. the first xml that appears in my answer). not sure what we are doing differentlyNazler
Could you please provide a minimal working example of an app with a working centred vector splash screen? E.g. a GitHub repo containing an Android Studio project? Just the bare minimum code to get a splash screen on API levels 21, 22 and 23?Ulceration
Also, are you sure that the Android Gradle Plugin isn't converting the vector resources to bitmap PNGs for you, and that the app is actually using a bitmap at runtime instead of the vector drawable you've put in the v21 res dir?Ulceration
@quealegriamasalegre: Are you sure about adding v23 and v24 folders? It just scaled in 21 and 22 in my emulator.Dragone
C
10

I'm currently working on a splash screen with a vector drawable centered both horizontally and vertically. Here is what I got on API level 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>

If you want to adjust the vector drawable's dimension, modify android:width and android:height attributes above, not necessarily modify the original drawable.

In addition, my experience is DO NOT put the vector drawable in a bitmap tag, which never works for me. Bitmap is for .png, .jpg and .gif format file, not for vector drawable.

Reference

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

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

Complexion answered 18/11, 2019 at 21:39 Comment(2)
Good catch about vector drawables needing to be inside an <item> tag instead of a <bitmap> tag.Brutify
in my case, it also worked for png image, but as you said bitmap tag not working, thanks too much :)Mantissa

© 2022 - 2024 — McMap. All rights reserved.