Reversing the morphing animation from arrow to che

2019-03-09 04:55发布

问题:

I am trying to achieve the morphing effect, when the user clicks myButton, the image inside the ImageView should morph from arrow to checkmark. And when he clicks it again, the process should reverse: the checkmark should morph to arrow.

This is what I have done:

animated_vector.xml:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_upvote">

    <target
        android:name="rotationGroup"
        android:animation="@anim/rotate_a_to_b" />

    <target
        android:name="upvote"
        android:animation="@animator/animator_upvote_to_checkmark" />
</animated-vector>

animator_upvote_to_checkmark.xml:

<objectAnimator
    android:duration="250"
    android:propertyName="pathData"
    android:valueFrom="@string/svg_upvote"
    android:valueTo="@string/svg_checkmark"
    android:valueType="pathType"
    android:repeatMode="reverse" />

And this is how I play the animation:

            Drawable drawable = upVote.getDrawable();
            if (drawable instanceof Animatable) {

                ((Animatable) drawable).start();
            }

This morphs the arrow into checkmark, how do I reverse the process?

回答1:

if your view is checkable and also your minsdk > 21 you can try using StateListAnimator with AnimatedVectorDrawable but because the appcompat now supports vectorDrawable and AnimatedVectorDrawable and also has a limitation on using all DrawableContainers I do not take above approach.

So let me tell you what may work:

DrawableContainers which reference other drawables resources which contain only a vector resource. For example, a StateListDrawable which references other files which contain a vector.

from Chris Banes

In order to achieve that I am going to create custom ImageView and every time it is clicked you must call morph function to run appropriate vectorAnimatorDrawable.

So code and demo:

public class ArrowToCheckMarkMorphingImageView extends ImageView {

    private AnimatedVectorDrawable mArrowToCheckMark;
    private AnimatedVectorDrawable mCheckMarkToArrow;
    private boolean mIsShowingCheckMark;
    public ArrowToCheckMarkMorphingImageView(Context context) {
        super(context);
        init();
    }

    public ArrowToCheckMarkMorphingImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ArrowToCheckMarkMorphingImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public ArrowToCheckMarkMorphingImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    public void init(){
        mIsShowingCheckMark = false;
        mArrowToCheckMark =
                (AnimatedVectorDrawable)getContext().getDrawable(R.drawable.arrow_to_checkmark);
        mCheckMarkToArrow =
                (AnimatedVectorDrawable)getContext().getDrawable(R.drawable.checkmark_to_arrow);
        setImageDrawable(mArrowToCheckMark);
    }

    public void morph(){
        final AnimatedVectorDrawable drawable
                = mIsShowingCheckMark ? mCheckMarkToArrow : mArrowToCheckMark;
        setImageDrawable(drawable);
        drawable.start();
        mIsShowingCheckMark = !mIsShowingCheckMark;
    }

}

and MainActivity:

public class MainActivity extends AppCompatActivity {

    ArrowToCheckMarkMorphingImageView mArrowToCheckMarkImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        mArrowToCheckMarkImageView = (ArrowToCheckMarkMorphingImageView)findViewById(R.id.imageView);
        mArrowToCheckMarkImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mArrowToCheckMarkImageView.morph();
            }
        });
    }
}

in layout file you must use it like:

  <yourpackage.ArrowToCheckMarkMorphingImageView
        android:id="@+id/imageView"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerInParent="true"
        />

and resources for those who wants to give it a try:

res/animator/arrow_to_checkmark

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="500"
        android:propertyName="pathData"
        android:valueFrom="M7.41,15.41 L12,10.83 l4.59,4.58 L18,14 18,14 l-6,-6 -6,6z"
        android:valueTo  ="M9,16.17 L4.83,12 l-1.42,1.41 L9,19 21,7 l-1.41,-1.41 0,0z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="500"
        android:propertyName="fillColor"
        android:valueFrom="#FF0000FF"
        android:valueTo="#FF00FF00" />
</set>

res/animator/checkmark_to_arrow

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="500"
        android:propertyName="pathData"
        android:valueFrom="M9,16.17 L4.83,12 l-1.42,1.41 L9,19 21,7 l-1.41,-1.41 0,0z"
        android:valueTo  ="M7.41,15.41 L12,10.83 l4.59,4.58 L18,14 18,14 l-6,-6 -6,6z"
        android:valueType="pathType" />
    <objectAnimator
        android:duration="500"
        android:propertyName="fillColor"
        android:valueFrom="#FF00FF00"
        android:valueTo="#FF0000FF" />
</set>

res/drawable/arrow_to_checkmark

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_arrow_up_24dp">
    <target
        android:animation="@animator/arrow_to_checkmark"
        android:name="arrow"/>
</animated-vector>

res/drawable/checkmark_to_arrow

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_checkmark_24dp">
    <target
        android:animation="@animator/checkmark_to_arrow"
        android:name="checkmark"/>
</animated-vector>

res/drawable/ic_arrow_up_24dp

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:name="arrow"
        android:fillColor="#FF0000FF"
        android:pathData="M7.41,15.41 L12,10.83 l4.59,4.58 L18,14 18,14 l-6,-6 -6,6z"/>
</vector>

res/drawable/ic_checkmark_24dp

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:name="checkmark"
        android:fillColor="#FF00FF00"
        android:pathData="M9,16.17 L4.83,12 l-1.42,1.41 L9,19 21,7 l-1.41,-1.41 0,0z"/>
</vector>


回答2:

I will go with Circular Progress Button, you can customize it based on your need or extend the library (since. it's open source). I have been using that for a while and it is pretty easy to integrate on your projects.

Also, you can learn from that and create your own library :-)

Here the Git repository CircularProgressButton

Also, you can try out other excellent open source such as Android Morphing Button