32

I have a VectorDrawable consists of 9 rectangles. This is defined as an XML in the the drawables folder. I have this set as the background for an ImageView that I have declared in xml. android:src="@drawable/squares00" I would like to change the color of one or more of the squares programatically at run time. I know there is way to do this using VectorDrawable animations. But I was wondering if there is simpler way of accessing my vectorDrawable in java, updating its properties (setting one or more of the fill colors for the rectangles) and then having the image background be updated with the updated VectoDrawable. My target is Android API 21 (lollipop)

Cœur
  • 37,241
  • 25
  • 195
  • 267
Jason Porter
  • 461
  • 1
  • 4
  • 8

3 Answers3

24

In short:

  1. You don't have a direct access to the inner elements in VectorDrawable.
  2. AnimatedVectorDrawable only has access to inner elements.
  3. Use AnimatedVectorDrawable to simulate what you need.

Long:

1. You don't have access

Looking at the source code for VectorDrawable will show that the inner elements information is stored in an inner private state class VectorDrawableState. The only method to expose the inner element by name is getTargetByName, but unfortunately it is package private (default) - you can't use it (unless you use reflection).

2. AnimatedVectorDrawable only has access

getTargetByName is only being used by AnimatedVectorDrawable, as we can find by searching for usages of the method.

3. Use AnimatedVectorDrawable

So now that we see that this is the only available option, for example, we can try the following to change the color of element "rect2" from white to black:

change_color.xml:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="0"
        android:propertyName="fillColor"
        android:valueFrom="@color/white"
        android:valueTo="@color/black"/>
</set>

animation.xml:

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/vectordrawable" >
    <target
        android:name="rect2"
        android:animation="@anim/change_color" />
</animated-vector>

and use the approach described here.

Note

If the above is still not an option for you, you can try the following:

  • Copy the entire VectorDrawable and tweak it (not tested)
  • Use reflection for getTargetByName to get the inner element. You will need to make sure to mutate the object first.
Eyal Biran
  • 5,656
  • 1
  • 28
  • 29
  • I see the package private method you mentioned in VectorDrawable: `Object getTargetByName(String name) { return mVectorState.mVPathRenderer.mVGTargetsMap.get(name); }` It looks like you are saying if I use this method, then I could theoretically perform an object animation on that object ("target") to change the color? What do you mean when you say I will need to `mutate` the object first? – Sam Edwards Aug 17 '15 at 19:40
  • 1
    If you use reflection to for getTargetByName, you will be able to get an element defined in the XML by name. One example will be of type VFullPath - which is also a private class. You will need to manipulate it using reflection again on the mFillColor field (to change color). Regarding the mutate, you must call mutate() on the drawable itself - that will create a local copy of the inner state. Before you do that all the same drawables share the same backing data to improve performance. – Eyal Biran Aug 18 '15 at 07:26
  • Thanks @Eyal. That makes sense. Looking forward to when they expose a public API for this. I could try this reflection hack for now, but am using a Webview with RapahelJS for now as a workaround. Have you messed with the support library that they're developing for VectorDrawable to see if this would work on that as well? https://android.googlesource.com/platform/frameworks/support/+/master/v7/vectordrawable/ – Sam Edwards Aug 18 '15 at 16:27
  • Can you please show how to use reflection on VectorDrawable to change the color of a specific path? – android developer Mar 23 '23 at 08:13
1

Use this library for direct access to the inner elements. It is implement this answer

Community
  • 1
  • 1
maXp
  • 1,428
  • 1
  • 15
  • 23
1

Try this library https://github.com/devendroid/VectorChildFinder

val vector = VectorChildFinder(context, R.drawable.drawable_id, imageView)
vector.findPathByName("path_name").fillColor = Color.RED

<vector
    ...>

    <path
        android:name="path_name"
        android:fillColor="#000"
        android:pathData="..." />
</vector>
KursikS
  • 176
  • 2
  • 8