How to use VectorDrawables in Android API lower than 21?
Asked Answered
C

13

77

I am working on an Android project and I chose <vector> to display icon because it is adaptable and dynamically, however, I just can run this app on devices running Android, which have API 21 or higher. My question is how can I use <vector> on lower Android version i.e. API 14 or kind of. Thanks!

<!-- drawable/ic_android_debug_bridge.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="48dp"
    android:width="48dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path android:fillColor="@color/primaryColorDark"
        android:pathData="M15,9A1,1 0 0,1 14,8A1,1 0 0,1 15,7A1,1 0 0,1 16,8A1,1 `0 0,1 15,9M9,9A1,1 0 0,1 8,8A1,1 0 0,1 9,7A1,1 0 0,1 10,8A1,1 0 0,1 9,9M16.12,4.37L18.22,2.27L17.4,1.44L15.09,3.75C14.16,3.28 13.11,3 12,3C10.88,3 9.84,3.28 8.91,3.75L6.6,1.44L5.78,2.27L7.88,4.37C6.14,5.64 5,7.68 5,10V11H19V10C19,7.68 17.86,5.64 16.12,4.37M5,16C5,19.86 8.13,23 12,23A7,7 0 0,0 19,16V12H5V16Z" /></vector>
Cathepsin answered 22/12, 2015 at 14:15 Comment(1)
G
31

Vector Drawables are now backward compatible, it's just a matter of upgrading your gradle version to 1.4.0-beta3 or higher, and upgrade your IDE :

We are also excited to offer backwards compatibility for your vector assets in Android Studio 1.4. Once you have a vectorDrawable image in your res/drawable, the Gradle plugin will automatically generate raster PNG images for API level 20 and below during build time. This means you only need to update and maintain your vector asset for your app project and Android Studio can take care of image conversion process.

http://android-developers.blogspot.com.uy/2015/09/android-studio-14.html

Gantz answered 22/12, 2015 at 14:22 Comment(3)
This is not entirely true. It will work only for imageView, and for the rest it will crash on old Android versions.Speakeasy
@androiddeveloper is there a solution to make it work for other views like EditText, and Buttons?Lardner
@RedM Yes, but only in code (Java/kotlin). Not in XML (of any kind), as it might cause crashes. To get VectorDrawable of the support library by code, use AppCompatResources.getDrawable . Do not use ResourcesCompat or ContextCompat for it. Read here: https://mcmap.net/q/112720/-resourcescompat-getdrawable-vs-appcompatresources-getdrawableSpeakeasy
R
101

With the support library 23.2, the true support for Vector Drawables has been provided all the way down to API v7. It is recommended to disable the previous version of the support, which rendered PNG during build-time, by adding

// Gradle Plugin 2.0+
 android {
   defaultConfig {
     vectorDrawables.useSupportLibrary = true
    }
 }

to the build.gradle file.

The implementation is fairly simple. Just use the new srcCompat attribute on Drawables (under app namespace!):

<ImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:srcCompat="@drawable/ic_add" />    // <= this is new

Vector Drawables are also supported in cases like TextView's drawableLeft property.

Source: library announcement

However, I would still recommend something like Iconics library, AndroidSVG, or another font-icon or SVG solution for the full SVG-standand vector support.

Rowdyism answered 4/3, 2016 at 12:20 Comment(3)
How do you do this programmatically?Rockandroll
If somebody have crash with android:drawableLeft you can try workaround: https://mcmap.net/q/112254/-how-to-use-vectordrawables-in-android-api-lower-than-21Lutestring
Yeah, I love you. Jokes apart, this is the perfect solution, thanks! Please if you gonna use this be aware app:srcCompat != android:srcCompat, you must use app: prefix in here.Marcin
O
97

VectorDrawable are supported pre-Lollipop via the Support Library, but the way to use them depends on the version of Support Library you have. And it may not work in all cases.

I've made this diagram to help (valid for Support Library 23.4.0 to - at least - 25.1.0).

VectorDrawable cheatsheet

Orchard answered 25/1, 2017 at 17:8 Comment(2)
This is the best answer ever.Funnelform
Unfortunately, build-time PNG generation doesn't support references to other resources, and will fail if you use things like android:fillColor="@color/dialog_button_text" in your vector drawables (so excluding the support library costs you more than just KBs). Error message points to developer.android.com/tools/help/vector-asset-studio.html for details.Corr
L
44

I found solution! For those who search solution with TextView and other "android" namespace attributes. First of all this is necessary:

android {  
   defaultConfig {  
     vectorDrawables.useSupportLibrary = true  
    }  
 }

And in application class define this:

    @Override
    public void onCreate() {
        super.onCreate();
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

Now you can use app:srcCompat="@drawable/ic_add" but if you try to use android:background= or android:drawableLeft= it will crash app with "Error inflating" exception.

We can create wrapped drawable ic_add_wrapped.xml for this vector:

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

And now it will work with any property like drawableLeft or background. Just set android:drawableLeft="@drawable/ic_add_wrapped.xml". WARNING! THIS IS A WORKAROUND SOLUTION. So you use it for your own risk.

Lutestring answered 10/11, 2016 at 9:6 Comment(22)
Does it work on all Android versions? Even old ones? Also, does it generate the "ic_add" as an image? If so, it misses the point of using vectors.Speakeasy
@androiddeveloper It work on all Android versions. Even old ones. Does app:srcCompat= generate the "ic_add" as an image?Lutestring
I asked if the flag in the gradle file generates the "ic_add" as an image. If it does, of course it will work on all Android versions, but it misses the point of vectors.Speakeasy
@androiddeveloper I really don't know if it generates an image. I tried to set small (12dp x 12dp) vector drawable to the background of some view with hardcoded big size (200dp x 200dp) and it looks smoothly and nice (looks like vector not as scaled bitmap). How to check if it generates image?Lutestring
just search for the file, either within the APK file itself, or in the folders of the app. I think it can be found, for example, on "...app\build\generated\res\pngs\debug\drawable-hdpi"Speakeasy
@androiddeveloper I extracted .apk file and double checked all directories in res folder. There are no .png generated files from my drawable wrapped as I described. There are only .xml files. Can you try this trick and confirm that it work as expected?Lutestring
OK, I now remember why I didn't use this solution: developer.android.com/reference/android/support/v7/app/… . "This feature defaults to disabled, since enabling it can cause issues with memory usage, and problems updating Configuration instances. If you update the configuration manually, then you probably do not want to enable this. You have been warned." . It's not recommended to use this feature. Also, as I remember, it could have some issues when having the VectorDrawable inside LayerDrawable on old Android versions.Speakeasy
@androiddeveloper It's better than just a crash.Lutestring
Yes, that's why you shouldn't use what you wrote. It's better to have extra png files, than having a crash on devices due to using this unfinished and buggy support library code, which also lacks many basic operationsSpeakeasy
@androiddeveloper It's better not use unfinished and buggy Android OS.Lutestring
Again, the support library docs say it's recommended you should not use the function you used, because it can cause issues. The Android OS can handle images very well, on all Android versions. By "unfinished" I meant that the code development has stopped in a problematic phase, so it's dangerous to use it. Also, as I wrote, I remember it lacks the capability of being used in LayerDrawable.Speakeasy
@androiddeveloper So you offer not to use vectors at all?Lutestring
No. I suggest that you use them in basic icons in the app itself, especially if you choose to use the flag that won't create images for you. You should be careful not to use the vectors in complex cases (like layerDrawable). Always check your app on pre-M versions of Android.Speakeasy
And also try to avoid the special, un-recommended, call to "AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)"Speakeasy
@androiddeveloper Without this workaround it does not work. So if you think that better to replace vector drawables by .png images then feel free to post your own answer to this question.Lutestring
As the docs say, it's limited to certain usages. The basic one that should work fine, is for ImageView to set its image . Read here: android-developers.blogspot.co.il/2016/02/… . They even marked the removed functionality with strikethrough line. Adding the function you called is not recommended. It can crash the app due to high memory usage on certain casesSpeakeasy
They even wrote : "As of plus.google.comAndroid Support Library 23.3.0, support vector drawables can only be loaded via app:srcCompat or setImageResource() " . In short, use it with caution, only in simple cases.Speakeasy
@androiddeveloper Now please scroll up to the top of the page and read question. Then scroll down to the bottom of the page and press "Add Another Answer". Type "DO NOT USE VECTOR DRAWABLE" and press enter.Lutestring
Why? I didn't say not not use it. I just said to use it carefully, and only in basic cases. If you wish to use it in all cases, you can disable the flag, and the images will be auto-generated, but this will miss the point of using it on old Android versions.Speakeasy
The layer-list wrapping trick doors not work in remote views (notifications, appwigets), which makes sense :-).Janka
This been bugging me for so long time in pre lollipop devices.Thanks a tons...!!Goodrow
@Debasish Say thanks for Google and their best practices of implementing software. Of course it's this solution its a dirty workaround and many of people already said it in comments.Lutestring
G
31

Vector Drawables are now backward compatible, it's just a matter of upgrading your gradle version to 1.4.0-beta3 or higher, and upgrade your IDE :

We are also excited to offer backwards compatibility for your vector assets in Android Studio 1.4. Once you have a vectorDrawable image in your res/drawable, the Gradle plugin will automatically generate raster PNG images for API level 20 and below during build time. This means you only need to update and maintain your vector asset for your app project and Android Studio can take care of image conversion process.

http://android-developers.blogspot.com.uy/2015/09/android-studio-14.html

Gantz answered 22/12, 2015 at 14:22 Comment(3)
This is not entirely true. It will work only for imageView, and for the rest it will crash on old Android versions.Speakeasy
@androiddeveloper is there a solution to make it work for other views like EditText, and Buttons?Lardner
@RedM Yes, but only in code (Java/kotlin). Not in XML (of any kind), as it might cause crashes. To get VectorDrawable of the support library by code, use AppCompatResources.getDrawable . Do not use ResourcesCompat or ContextCompat for it. Read here: https://mcmap.net/q/112720/-resourcescompat-getdrawable-vs-appcompatresources-getdrawableSpeakeasy
T
20

you need use android Support Repository 30+ if you using android studio and need android support library 23.2.1+ if using Eclipse.

check your build.gradle (project) if using version 2.0+ add below code in your build.gradle (app)

// Gradle Plugin 2.0+  
 android {  
   defaultConfig {  
     vectorDrawables.useSupportLibrary = true  
    }  
 }

and: if using version 1.5 add below in your build.gradle(app)

// Gradle Plugin 1.5  
 android {  
   defaultConfig {  
     generatedDensities = []  
  }  

  // This is handled for you by the 2.0+ Gradle Plugin  
  aaptOptions {  
    additionalParameters "--no-version-vectors"  
  }  
 }  

here is sample code for use vector icon:

<ImageView  
  android:layout_width="wrap_content"  
  android:layout_height="wrap_content"  
  app:srcCompat="@drawable/ic_add"
  tools:ignore="MissingPrefix" />

or

<ImageButton
  android:layout_width="wrap_content"
  android:background="@android:color/transparent"
  app:srcCompat="@drawable/camera"
  tools:ignore="MissingPrefix"
  android:layout_height="wrap_content"/>

Vector Drawables are also supported in cases like TextView's drawableLeft property. but it worked api 22+ to me and i still dont know how it will work for low api.

Also keep in mind if you want to be compatible below API 21:

  • you cannot use the android:background property in xml or View.setBackgroundResource() function. You need to use the View.setBackground().
  • you cannot use the svg-s in StateListDrawable xml-s or other xml drawables, you have to generate them programmatically.
  • you cannot use svg-s in case of notifications.
Terminus answered 16/4, 2016 at 7:31 Comment(4)
For TextView, it will crash.Speakeasy
@androiddeveloper Here you are: https://mcmap.net/q/112254/-how-to-use-vectordrawables-in-android-api-lower-than-21Lutestring
complete answer :). that is what i m looking for.Muniments
Dude you've saved from changing all my SVGs to PNGs well done sirTriceratops
C
19

When you need to add VectorDrawable (created from SVG) programatically, you can do it like this:

icon = VectorDrawableCompat.create(resources, R.drawable.ic_map_black_24dp, null)
Cosec answered 19/12, 2016 at 11:33 Comment(3)
Wait, this means we can use SVG files in the project? No need to convert them to VectorDrawable in the IDE ?Speakeasy
Of course, we need. R.drawable.ic_map_black_24dp here is a result of SVG-source conversion.Cosec
what is the resources argument in the snippet above?Discriminatory
M
9

For lower version compatible,

  1. add the below in gradle,

    android {  
       defaultConfig {  
         vectorDrawables.useSupportLibrary = true  
        }  
     }
    
  2. add the below code in onCreate() in your application class,

    @Override
        public void onCreate() {
            super.onCreate();
            AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        }
    
  3. In xml for imageView,

    <ImageView
        android:id="@+id/imageViewMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/transparent"
        app:srcCompat="@drawable/ic_success"/>
    
  4. If you want to change the image source programmatically use this,

    imageView.setImageResource(R.drawable.ic_launcher);
    
Morbidity answered 12/7, 2018 at 9:31 Comment(2)
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); is this necessary ?Ginnifer
What about EditText where I want to use drawableStart ?Islington
I
4

I had an issue where my vector images would show up but would be completely black, this was due to an issue where you can't reference color resources in the vector xml file.

So instead of @color/primaryColorDark you have to use the actual color e.g. #212121

Ineffective answered 20/1, 2017 at 13:19 Comment(0)
I
2

Just to make @2Dee's answer complete:

VectorDrawable cannot be used on API 20 and lower. The official method generates pngs, which then become BitmapDrawables, which breaks the idea of vector graphics.

Personally I prefer svg's over xml vectors:

  • such graphics can be exported right from Illustrator or other popular software
  • svg supports transformations, text, masks and other things properly
  • true scallable vector graphics on all platforms
  • smaller size and faster builds

To use vector graphics you can try https://github.com/BigBadaboom/androidsvg . It's an svg reader and svg-compatible ImageView.

Ichnite answered 28/12, 2015 at 16:26 Comment(0)
H
2

If you are using Android Studio 3.0.0 you can set

android.enableAapt2=false 

in gradle.properties

https://www.reddit.com/r/androiddev/comments/6mj8di/android_studio_30_canary_6_released/

Hexamethylenetetramine answered 25/7, 2017 at 1:10 Comment(0)
B
2

You can use programatically ..to set drawableLeft to your editText or textView

like

Drawable tick_drawable = VectorDrawableCompat.create(getResources(), R.drawable.green_tick, null);
    if (tick_drawable != null) {
        tick_drawable.setBounds(0, 0, tick_drawable.getIntrinsicWidth(),tick_drawable.getIntrinsicHeight());
    }

And to drawable left like this..

editText.setCompoundDrawables( tick_drawable , null, null, null );
Bountiful answered 27/2, 2018 at 9:22 Comment(1)
getting tick_drawable value null on android 5.1Thorr
C
1

after setting vectorDrawables.useSupportLibrary = true in gradle default and AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); in activity on create

To avoid crash with android:drawableleft in Textview, set drawble left to the textview programitically for examlple:

 textview.setCompoundDrawablesWithIntrinsicBounds(R.drawable.movie, 0, 0, 0);
Collenecollet answered 2/3, 2019 at 17:41 Comment(0)
P
0

I also found the same issue. And I did:

vectorDrawables.useSupportLibrary = true

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

textview.setCompoundDrawablesWithIntrinsicBounds(R.drawable.movie, 0, 0, 0);

It is good. But after that, I got an error that the resource not found.
So I found it's better to add a vector image according to the SDK version.

Finally, this solution became good is for me:-

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
    textview.setCompoundDrawablesWithIntrinsicBounds(null, null, AppCompatResources.getDrawable(this, R.drawable.movie), null);
} else {
    textview.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.movie, 0);
}
  • I hope, it will help someone.
Pimbley answered 13/11, 2019 at 5:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.