I'm trying to do an animation like youtube Play/Pause with svgs paths and animated vector.
Strings.xml
<resources>
<string name="app_name">AnimatedSvgTest</string>
<string name="svg_triangle">
M0,0 L0,24 12,12 0,0
</string>
<string name="svg_pause">
M0,0 L0,24 M12,0 L12,24
</string>
</resources>
anim/path_morph.xml
:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="4000"
android:propertyName="pathData"
android:valueFrom="@string/svg_triangle"
android:valueTo="@string/svg_pause"
android:valueType="pathType" />
</set>
drawable/avd.xml
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_play_arrow_24dp">
<target
android:name="play"
android:animation="@anim/path_morph" />
</animated-vector>
drawable/ic_pause_arrow.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:pathData="@string/svg_pause"
android:strokeColor="#000"
android:strokeWidth="1.0" />
</vector>
drawable/ic_play_arrow.xml
:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportHeight="100.0"
android:viewportWidth="100.0">
<path
android:fillColor="#FF000000"
android:pathData="@string/svg_triangle" />
</vector>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<ImageButton
android:id="@+id/button"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:src="@drawable/avd" />
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageButton button = (ImageButton) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animatable animatable = (Animatable) button.getDrawable();
animatable.start();
}
});
}
}
But I still get Caused by: android.view.InflateException: Binary XML file line #2 Can't morph from
crash :/
One way to animate Drawables is to load a series of Drawable resources one after another to create an animation. This is a traditional animation in the sense that it is created with a sequence of different images, played in order, like a roll of film. The AnimationDrawable class is the basis for Drawable animations.
There are two types of animations that you can do with the view animation framework: Tween animation: Creates an animation by performing a series of transformations on a single image with an Animation. Frame animation: or creates an animation by showing a sequence of images in order with an AnimationDrawable .
In order to do this, you need to make the paths compatible. There will be multiple ways to do this, depending on what you want the actual transition to look like, you basically use some tricks to make sure that the pathData
of these two drawables have the same number and type of commands in the same order. In this case that means that one triangle is made using the same format of path as two rectangles.
An additional problem here is that in the case of the triangle you are using fillColor and so what you see on screen is what has been filled inside the path you draw, but the pause symbol is using lines with thickness 1.0, so what you see on screen are actually two thick lines rather than filled in shapes.
I suggest you should change this so that both use a fillColor instead of coloured, thick lines, then make the paths compatible.
Here's one example of how to make these compatible. Currently the incompatible paths look like this:
<string name="svg_triangle">
M0,0 L0,24 12,12 0,0
</string>
<string name="svg_pause">
M0,0 L0,24 M12,0 L12,24
</string>
These are not compatible because they do not have the same number of M
& L
commands or the same number of coordinates. Compatible paths would look like this:
<string name="svg_triangle">
M0,0 L0,24 12,12 0,0 M12,0 L12,0 12,0 12,0
</string>
<string name="svg_pause">
M0,0 L0,24 1,24 1,0 M12,0 L12,24 13,24 13,0
</string>
You should notice that these now have the same numbers of M
and L
commands with the same number of coordinates.
I've added M12,0 L12,0 12,0 12,0
to the triangle's path; this part is not visible on screen as it doesn't really outline a shape, all of the points are at coordinate 12,0.
For the pause button I've changed it a bit more since before it was just two lines, I've now outlined the shapes instead. For this to look right, change the <path>
section of drawable/ic_pause_arrow.xml
to use fillColor the same as the play one, so:
<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="@string/svg_pause"/>
</vector>
You should now have two vector drawables compatible for morphing. Note I haven't specifically tried out the transition, it may not look exactly like you want it to, so a bit of experimentation might be needed. I wrote a tutorial on my blog which I think is relevant where I detail some more complex examples of this, with more background about the issues and techniques including source code. Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With