22

UPDATE: Solution found! Scroll down for my accepted answer!

I want to animate multiple elements of one image and link the animation to ViewPagers position (so multiple elements are morphing or flying in/out depending on the current page being dragged).

So, is there a way to precisely control the current frame of the animation? For example let's assume i have this set:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="800"
        android:propertyName="scaleY"
        android:valueFrom="0"
        android:valueTo="1" />
    <objectAnimator
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="800"
        android:propertyName="scaleX"
        android:valueFrom="0"
        android:valueTo="1" />
    <objectAnimator
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="800"
        android:propertyName="rotation"
        android:valueFrom="0"
        android:valueTo="360" />
</set>

Animated vector file:

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/pre_signup_1" >
    <target
        android:name="plus_button"
        android:animation="@anim/pre_signup_1_plus_container" />
    <target
        android:name="plus"
        android:animation="@anim/pre_signup_1_plus_sign" />
</animated-vector>

Java code to run the animation:

ImageView mImage1 = (ImageView) findViewById(R.id.image_1);
AnimatedVectorDrawableCompat animated = (AnimatedVectorDrawableCompat) mImage1.getDrawable();

animated.start();

Is there a way to control the animation like setCurrentDuration(400) which will presumably set the current state of the animation to it's half? Maybe there is a way to split that vector drawable into layers and animate them programmatically? Thanks in advance!

0

3 Answers 3

13

Looks like it is possible to access paths and groups inside VectorDrawableCompat and animate/morph them however you want!

After a some research I ended up duplicating the following classes from the android.support.graphics.drawable package: AndroidResources, PathParser, TypedArrayUtils, VectorDrawableCommon and VectorDrawableCompat.

Next we need to make the following method and classes public inside of the VectorDrawableCompat class: getTargetByName, VGroup and VFullPath.

Next in the VectorDrawableCompat class remove the block that checks for Android version (Build.VERSION.SDK_INT >= 23). Don't know why, but if you don't do it, the animating won't work on android API 23 and up (need more research).

I may have missed a couple of private methods, but it's just a matter of making them public if you run into problems.

So, now we have access to the layers of our VectorDrawable! Here is a small example of scaling a vector group depending on the ViewPager's position:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="276dp"
    android:height="359dp"
    android:viewportWidth="276"
    android:viewportHeight="359">

    <group
        android:pivotX="205.5"
        android:pivotY="214.5"
        android:name="my_group">
        <path
            android:strokeColor="#4D394B"
            android:strokeWidth="7"
            android:strokeLineJoin="bevel"
            android:fillColor="#1ED761"
            android:pathData="M206.5,180 C186.9,180,171,195.9,171,215.5 S186.9,251,206.5,251
C226.1,251,242,235.1,242,215.5 S226.1,180,206.5,180 Z" />
        <path
            android:fillColor="#4D394B"
            android:pathData="M210,211 L210,190 L202,190 L202,211 L181,211 L181,219 L202,219 L202,241 L210,241
L210,219 L232,219 L232,211 Z" />
    </group>
</vector>

And this is the code used to animate the group:

ImageView myImageView;
VectorDrawableCompat.VGroup myVectorGroup;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_welcome);

    myImageView = (ImageView) findViewById(R.id.image_1);

    VectorDrawableCompat vectorDrawable = VectorDrawableCompat.create(getResources(), R.drawable.my_vector_drawable, null);
    vectorDrawable.setAllowCaching(false); // Important to allow image updates
    myVectorGroup = (VectorDrawableCompat.VGroup) vectorDrawable.getTargetByName("my_group");

    myImageView.setImageDrawable(vectorDrawable);

    mViewPager = (ViewPager) findViewById(R.id.pager);
    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if(myVectorGroup != null && position < 1) {
                myVectorGroup.setScaleX(1f - positionOffset);
                myVectorGroup.setScaleY(1f - positionOffset);

                myImageView.invalidate();
            }
        }
    });
}

I need some more testing to determine compatibility, but it works for now!

8
  • 2
    Not tried this yet, but nice work. It's a shame the AnimatedVectorDrawable is missing this ability out the box. Commented Aug 25, 2016 at 14:29
  • Yeah, that's what I'm talking about! :( I use this possibility all over the place now and with really complex animations! And it's quite fast even on older devices!
    – Arthur
    Commented Aug 26, 2016 at 11:29
  • 1
    @Arthur I don't see any code to interpolate path data after you modified the existing code for vectorDrawable. Can you please share the whole code to clearly understand how you achieved pathData morph in pre lollipop device i.e API 15 above
    – Aftab Ali
    Commented Sep 8, 2016 at 19:33
  • @AftabAli Hi! If you need to morph one shape to another you need both shapes to have the same number of commands (they should be drawn using same vertices in you vector software). After that you can use getPathData from VPath which will return all the commands for needed path. Then you need to call interpolatePathDataNode on each path node from the array and morph it accordingly.
    – Arthur
    Commented Sep 20, 2016 at 11:28
  • i must to say "Thank you very much!" Big thanks for tricks. It's worked for me!
    – hungtdo
    Commented Aug 31, 2017 at 17:38
2

I believe you can't do this with the AnimatedVectorDrawable/AnimatedVectorDrawableCompat programmatically.

A workaround is to use a Level List drawable and animate its levels: but you'll need lots of images for the levels to simulate the smooth transition, I'm using just three levels here for demonstration.

First you'll define the drawable level list like this:res/drawable/my_level_list.xml

<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/level0"
    android:maxLevel="0"/>
    <item android:drawable="@drawable/level1"
        android:maxLevel="1"/>

    <item android:drawable="@drawable/level2"
        android:maxLevel="2"/>
</level-list>

Then in the layout file:

<ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/my_level_list" />

Then in your activity:

int MIN_LEVEL=0;
int MAX_LEVEL=2;

ImageView img=(ImageView)findViewById(R.id.img);

Drawable myLevelSetDrawable= img.getDrawable();
ObjectAnimator anim=ObjectAnimator.ofInt(myLevelSetDrawable,"level",MIN_LEVEL,MAX_LEVEL);
anim.setInterpolator(new BounceInterpolator());
AnimatorSet set=new AnimatorSet();
set.play(anim);
set.setDuration(1000);
set.start();

Hope that helps.

1

As far as the documentation says for AnimatedVectorDrawables there is no way to track the state of animation or to jump into any state other than animationStart and animationEnd.

You may have to use a custom solution to achieve this effect. This blog post explains a way using Android’s widget state tracking to achieve a similar effect.

1
  • Maybe there is another way to animate objects back and forth? I don't mean vector groups, but maybe separate ImageViews?
    – Arthur
    Commented May 12, 2016 at 10:22

Not the answer you're looking for? Browse other questions tagged or ask your own question.