Youtube Play/pause Animated Vector Drawable on and

2019-03-17 01:44发布

问题:

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 :/

回答1:

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.