4

I am having some trouble with vectors drawable on older API version in Android. I need to change drawables at runtime each time the activity starts should load the corresponding svg file.

This is my layer list :

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

<item >
    <shape android:shape="oval">
        <size android:height="50dp" android:width="50dp"/>
        <!-- fill color -->
        <solid android:color="@color/white" />
    </shape>
</item>
<item
    android:id="@+id/avatar"
    android:drawable="@drawable/dog" //I need to change this at run time
    android:bottom="10dp"
    android:left="10dp"
    android:right="10dp"
    android:top="10dp"/>

In my activity, I am using

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

So far I am using layer drawable to pick the xml file and change the svg but I am having compatibility issues as layerDrawable.setDrawble() is only available in API level >= 23

 layerDrawable = (LayerDrawable) ContextCompat.getDrawable(this, R.drawable.seekbar_thumb);
 Drawable avatar = (Drawable) ContextCompat.getDrawable(this, getUserAvatarId());
 layerDrawable.setDrawable(0, avatar);
Vasco
  • 297
  • 3
  • 16
  • Have you tried ``app:srcCompat`` instead of ``android:drawable``? that's how vectors drawables must be added to views, i haven't try into xml resources files. – Gonzalo Jan 26 '18 at 13:37
  • @Gonzalo I can't add the `app:srcCompat` to xml resources file, but this, not the issue, as I mentioned is that I want to change the vector at runtime from the layer list – Vasco Jan 26 '18 at 13:43
  • 1
    You can't use layerDrawable with VectorDrawable within it for old Android versions. Do it in code instead. Hard and annoying, but possible. As for using VectorDrawable, you need to fetch it using `AppCompatResources.getDrawable()` – android developer Jan 30 '18 at 09:10
  • @android developer could you provide a sample? – Vasco Jan 30 '18 at 09:16
  • @VasilVasilev I don't have time for this right now. Maybe this could help: https://stackoverflow.com/a/44981662/878126 ? – android developer Jan 30 '18 at 10:19
  • @androiddeveloper it doesn't really help. – Vasco Jan 31 '18 at 10:22
  • @VasilVasilev OK I've put an answer, and also tested on Android 4.4 emulator. BTW, I think I was mistaken for where to put the VectorDrawable. Shouldn't be in anydpi. Should be in nodpi. Otherwise it can't find it. – android developer Jan 31 '18 at 16:45

2 Answers2

1

The solution of this is simple. The concept is that I wanted to change the thumb of a seek bar in the onCreate() state of an activity. The thumb of my seek bar is drawable file the one I posted. Basically, it loads an SVG file with a white stroke around it, depending on the user this SVG should be changeable.

For Android API >= 16 the solution is the following:

layerDrawable = (LayerDrawable) ContextCompat.getDrawable(this, R.drawable.seekbar_thumb);
Drawable avatar = VectorDrawableCompat.create(getResources(), getUserAvatarId(), null);
layerDrawable.mutate(); //not share its state with other drawables
layerDrawable.setDrawableByLayerId(R.id.avatar, avatar);
layerDrawable.invalidateSelf(); //inform the layer drawable when it needs to redraw
seekBar.setThumb(layerDrawable);

The Function setDrawableById(int, drawable) is of type boolean and returns either true if the drawable of the layer is changed or false if is not! Then you need to replace the view with the new drawable file in this case seekBar.setThumb(layerDrawable);

Vasco
  • 297
  • 3
  • 16
1

You can't use LayerDrawable with VectorDrawable in it, on old Android versions.

You can do it programmatically . Example:

MainActivity.kt

    ...
    val border = ShapeDrawable()
    border.paint.color = Color.WHITE
    val background = ShapeDrawable()
    background.paint.color = Color.BLACK
    val vectorDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_android_black_24dp)!!
    val layers = arrayOf<Drawable>(background, border, vectorDrawable)
    val layerDrawable = LayerDrawable(layers)
    layerDrawable.setLayerInset(0, 0, 0, 0, 0)
    layerDrawable.setLayerInset(1, 1, 0, 1, 1)
    layerDrawable.setLayerInset(2, 0, 0, 0, 10)
    imageView.setImageDrawable(layerDrawable)

res/drawable/nodpi/ic_android_black_24dp.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp"
        android:viewportHeight="24.0" android:viewportWidth="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
</vector>

build.gradle

...
android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "lb.com.myapplication"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = true
    }
 ...

The result, on Android 4.4.2 emulator:

enter image description here

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • Thank you for the provided answer it is also helpful. As you said it's a bit hard and annoying as you are creating the layer drawable programmatically. However, I found the solution and posted as an answer if you want to have a look. Thanks anw! – Vasco Feb 02 '18 at 08:09