Android 属性动画来实现帧动画的炫酷效果

来源:互联网 发布:mac为什么会全灭 编辑:程序博客网 时间:2024/05/29 13:45

         最近公司让实现一个很炫的动画,最先想到的是使用帧动画,后来想到图片太多,使用帧动画太耗性能,由于图片多还很可能出现oom,所以决定使用属性动画来实现,下面是动画的效果图

        :

       下面来说一下思路:

       首先根据动画的效果来写一下布局文件:

<?xml version="1.0" encoding="utf-8"?><FrameLayout 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"    tools:ignore="RtlHardcoded,ContentDescription">    <!--way-->    <ImageView        android:id="@+id/iv_way1"        android:layout_width="56dp"        android:layout_height="155dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginBottom="110dp"        android:layout_marginRight="50dp"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_way2"        android:layout_width="56dp"        android:layout_height="162dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginBottom="200dp"        android:layout_marginRight="35dp"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_way3"        android:layout_width="61dp"        android:layout_height="157dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginBottom="160dp"        android:layout_marginLeft="20dp"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_way4"        android:layout_width="104dp"        android:layout_height="111dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginBottom="110dp"        android:layout_marginLeft="20dp"        android:visibility="gone" />    <!--fire-->    <ImageView        android:id="@+id/iv_fire1"        android:layout_width="119dp"        android:layout_height="119dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginBottom="200dp"        android:layout_marginRight="62dp"        android:scaleType="centerCrop"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_fire2"        android:layout_width="97dp"        android:layout_height="97dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginBottom="310dp"        android:layout_marginRight="50dp"        android:scaleType="centerCrop"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_fire3"        android:layout_width="245dp"        android:layout_height="245dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginBottom="180dp"        android:layout_marginLeft="40dp"        android:scaleType="centerCrop"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_fire4"        android:layout_width="103dp"        android:layout_height="103dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginBottom="168dp"        android:layout_marginLeft="65dp"        android:scaleType="centerCrop"        android:visibility="gone" />    <!--castle-->    <LinearLayout        android:id="@+id/ll_castle"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center|bottom"        android:layout_marginBottom="30dp"        android:orientation="vertical"        android:visibility="gone">        <include            layout="@layout/view_gift_name"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal"            android:layout_marginBottom="20dp" />        <ImageView            android:id="@+id/iv_castle"            android:layout_width="207dp"            android:layout_height="192dp"            android:scaleType="centerCrop"            android:visibility="gone" />    </LinearLayout>    <!--light-->    <ImageView        android:id="@+id/iv_light1"        android:layout_width="74dp"        android:layout_height="254dp"        android:layout_gravity="bottom|left"        android:layout_marginBottom="60dp"        android:layout_marginLeft="-37dp"        android:rotation="50"        android:scaleType="centerCrop"        android:transformPivotX="37dp"        android:transformPivotY="254dp"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_light2"        android:layout_width="74dp"        android:layout_height="254dp"        android:layout_gravity="bottom|center_horizontal"        android:layout_marginRight="80dp"        android:rotation="30"        android:scaleType="centerCrop"        android:transformPivotX="37dp"        android:transformPivotY="254dp"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_light3"        android:layout_width="74dp"        android:layout_height="254dp"        android:layout_gravity="bottom|center"        android:layout_marginLeft="80dp"        android:rotation="-30"        android:scaleType="centerCrop"        android:transformPivotX="37dp"        android:transformPivotY="254dp"        android:visibility="gone" />    <ImageView        android:id="@+id/iv_light4"        android:layout_width="74dp"        android:layout_height="254dp"        android:layout_gravity="bottom|right"        android:layout_marginBottom="60dp"        android:layout_marginRight="-40dp"        android:rotation="-50"        android:scaleType="centerCrop"        android:transformPivotX="40dp"        android:transformPivotY="254dp"        android:visibility="gone" /></FrameLayout>
       上面是根据动画效果写的xml布局,布局分为四部分,1 烟花燃放的路径,2 烟花,3 城堡,4 灯光,这四部分是根据动画的效果来进行设计的。

       接下来使用属性动画来实现,使用属性动画之前,要知道几个类,以及这些类的一些方法:

       一  Animator

      This is the superclass for classes which provide basic support for animations which can be started, ended, and have AnimatorListeners added to them.

       这是一个提供基础动画支持的,用来开始,结束和添加动画监听器的超类。

       直接子类:AnimatorSet,ValueAnimator

       间接子类:ObjectAnimator,TimeAnimator

       内部类:Animator.AnimatorListener,Animator.AnimatorPauseListener

       常量:DURATION_INFINITE 这是一个用来定义动画执行的无穷的持续时间的参数 long类型 例如当动画重复执行的时候就可以使用这个参数

       构造函数:Animator()

       下面是公有的方法:

       1 addListener(Animator.AnimatorListener listener) 无返回值,它是用来添加一个监听对于动画的执行的整个生命周期,可以监听动画的开始,重复,结束等等。

       2 addPauseListener(Animator.AnimatorPauseListener listener) 无返回值,它是用来添加一个暂停的监听,当动画暂停的时候,可以触发这个监听。       

       3 cancel() 无返回值,它是用来取消动画的,它和end()是不同的,它用自己的方式导致动画停止,通过发送一个onAnimationCancel(Animator),紧跟在onAnimationEnd(Animator)之后。    

       4 clone() 返回值是Animator,它是用来创建返回一个动画对象。

       5 end() 无返回值,它是用来结束动画的,它通过分配到执行动画的属性的结束参数,然后通过调用onAnimationEnd(Animator)来结束动画。

       6 getDuration() 抽象方法,返回值是long类型,它能够得到动画的执行持续时间。

       方法有点多,就不一一写了,自己去看吧^_^

       二  ObjectAnimator 这是Animator 的间接子类,它继承ValueAnimator,ValueAnimator又继承Animator.

       下面只介绍用到的一些方法,其他的方法自己去看哈.

       ofFloat(Object target, String propertyName, float... values) 返回值是静态的ObjectAnimator对象。

      Constructs and returns an ObjectAnimator that animates between float values. A single value implies that that value is the one being animated to, in which case the start value will be derived from the property being animated and the target object when start() is called for the first time. Two values imply starting and ending values. More than two values imply a starting value, values to animate through along the way, and an ending value (these values will be distributed evenly across the duration of the animation).

     结合有道词典,我来翻译一下哈,float ... values 这个参数比较有意思,它是一个数组,通常我们使用的时候会传一个起始值和一个结束值,String propertyName 这个参数是要执行的属性动画的名字,例如rotation(旋转),alpha(透明度),scaleX(x轴方向的缩放),scaleY(y轴方向的缩放),x(x轴方向的移动),y(y轴方向的移动),Object target参数就是要执行动画的物体目标,也就是view,理解了这些参数后,整体就好翻译了,意思就是构建返回一个动画,这个动画按照value来执行,如果只有一个value的话,这个value是一个将要被执行动画的value,在这种情况下,开始的value将要被获得从被执行的属性动画的目标身上当start()方法被第一次调用的时候。如果有两个value的话,那个一个value是开始,另一个是结束,如果超过两个value的话,一个是开始的value,中间的values是动画沿着路径执行过程中的value,最后一个是结束时的value.

      三 PropertyValuesHolder 这是一个可以和ObjectAnimator,ValueAnimator结合使用,可以创建一些复杂的组合动画的类。

          ofFloat(String propertyName, float... values) 返回一个静态的PropertyValuesHolder对象,这个方法的用法和上面介绍的ObjectAnimator的ofFloat()的用法是类似的,只是缺少了第一个target参数,这些就不介绍了

      四 AnimatorSet 它是干什么的呢,上面的propertyvaluesholder可以实现单个view的简单的组合动画,比如旋转,缩放和移动同时执行,那么这个类就更加niu了,它可以把所有的view的动画进行组合,可以实现更加复杂的组合动画。

      play(Animator anim)先说一下这个方法,它的返回值是AnimatorSet.Builder,通过这个builder可以把不同的动画按照时间先后的顺序组合在一起,也就是用它来建立一些动画执行的约束条件。

     五 AnimatorSet.Builder 

     after(long delay) 返回值是AnimatorSet.Builder对象,通过这个方法可以使play(animator)中的animator延迟delay时间后再执行。

     after(Animator anim) 返回值是AnimatorSet.Builder对象,通过这个方法可以使play(animator)中的animator在after(Animator anim)中的anim执行后再执行。

     before(Animator anim) 返回值是AnimatorSet.Builder对象,通过这个方法可以使play(animator)中的animator在before(Animator anim)中的anim执行之前再执行。

  with(Animator anim)返回值是AnimatorSet.Builder对象,通过这个方法可以使play(animator)中的animator与with(Animator anim)中的anim同时执行。

    上面把用到的一些类以及这些类中的一些方法介绍完了,下面就开始去敲代码实现。

    动画中分为四部分,烟花,燃放烟花的路径,城堡,灯光,我们可以把烟火和路径放到一起来实现

    首先先来实现烟花燃放的动画:

    private Animator getFireWorkAnimator(final ImageView way, final int wayHeight, final ImageView fire) {        ValueAnimator wayAni = ValueAnimator.ofFloat(0f, ViewUtil.dp2Px(way.getContext(), wayHeight));        wayAni.setDuration(300);        wayAni.addListener(new ViewShowListener(way));        wayAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            int lastHeight;            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                Float value = (Float) valueAnimator.getAnimatedValue();                way.getLayoutParams().height = value.intValue();                lastHeight = value.intValue();                way.requestLayout();            }        });        ObjectAnimator wayScaleAni = ObjectAnimator.ofFloat(way, "alpha", 1, 0.2f);        wayScaleAni.setDuration(500);        PropertyValuesHolder alphaValue = PropertyValuesHolder.ofFloat("alpha", 0, 1);        PropertyValuesHolder scaleXValue = PropertyValuesHolder.ofFloat("scaleX", 0, 1);        PropertyValuesHolder scaleYValue = PropertyValuesHolder.ofFloat("scaleY", 0, 1);        ObjectAnimator fireAni = ObjectAnimator.ofPropertyValuesHolder(fire, alphaValue, scaleXValue, scaleYValue);        fireAni.setDuration(600);        fireAni.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animator) {                fire.setVisibility(View.VISIBLE);                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        way.setVisibility(View.GONE);                    }                }, 300);            }            @Override            public void onAnimationEnd(Animator animator) {                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        fire.setVisibility(View.GONE);                    }                }, 300);            }            @Override            public void onAnimationCancel(Animator animator) {            }            @Override            public void onAnimationRepeat(Animator animator) {            }        });        AnimatorSet set = new AnimatorSet();        set.play(wayAni).with(wayScaleAni);        set.play(wayScaleAni).before(fireAni);        set.play(fireAni);        return set;    }
       然后再实现城堡的动画:

       

   private Animator getCastleShowingAnimator() {        float castleSY = ViewUtil.getScreenHeight(context) + ViewUtil.dp2Px(context, 240);        float castleDY = ViewUtil.getScreenHeight(context) - ViewUtil.dp2Px(context, 240) - ViewUtil.dp2Px(context, 30);        PropertyValuesHolder castleX1 = PropertyValuesHolder.ofFloat("y", castleSY, castleDY);        PropertyValuesHolder castleScaleX = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1.0f);        PropertyValuesHolder castleScaleY = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1.0f);        ObjectAnimator castleAni = ObjectAnimator.ofPropertyValuesHolder(llCastle, castleX1, castleScaleX, castleScaleY);        castleAni.addListener(new ViewShowListener(ivCastle));        castleAni.addListener(new ViewShowListener(llCastle));        castleAni.setInterpolator(new OvershootInterpolator());        castleAni.setDuration(1000);        return castleAni;    }
      城堡的动画比较简单些,最后我们来实现灯光的动画:

      

   private Animator getLightersAnimator() {        Animator light1 = getLighterAnimator(ivLight1, -15, 20, -30, 50);        Animator light2 = getLighterAnimator(ivLight2, 0, -50, 30, 30);        Animator light3 = getLighterAnimator(ivLight3, 0, 50, -30, -30);        Animator light4 = getLighterAnimator(ivLight4, -15, -30, 10, -55);        AnimatorSet set = new AnimatorSet();        set.play(light1).with(light4);        set.play(light4);        set.play(light2).with(light3).after(200);        return set;    }    private Animator getLighterAnimator(ImageView lighter, int rotation1, int rotation2, int rotation3, int rotation4) {        Animator ani1 = getLighterAnimator(lighter, rotation1, rotation2);        Animator ani2 = getLighterAnimator(lighter, rotation2, rotation3);        Animator ani3 = getLighterAnimator(lighter, rotation3, rotation4);        AnimatorSet set = new AnimatorSet();        set.addListener(new ViewShowListener(lighter));        set.play(ani1).before(ani2);        set.play(ani2).before(ani3);        return set;    }    private Animator getLighterAnimator(ImageView lighter, int origin, int destination) {        ObjectAnimator lighterAni = ObjectAnimator.ofFloat(lighter, "rotation", origin, destination);        lighterAni.setDuration(800);        return lighterAni;    }

下面是自定义的animator监听器:

       

    class ViewShowListener implements Animator.AnimatorListener {        private View view;        public ViewShowListener(View view) {            this.view = view;        }        @Override        public void onAnimationStart(Animator animator) {            view.setVisibility(View.VISIBLE);        }        @Override        public void onAnimationEnd(Animator animator) {        }        @Override        public void onAnimationCancel(Animator animator) {        }        @Override        public void onAnimationRepeat(Animator animator) {        }    }

基本上动画的效果就实现了,在做的过程中会有很多细节的动画,比如每个动画的执行的时间,执行的路径,这些都要和设计动画的ui讨论商量的,想要做出这个动画,也要两天时间的。

下面我把完成的代码贴出来,然后把demo的下载地址贴出来。

package com.guoliuya.propertyanimationdemo;import android.animation.Animator;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.animation.PropertyValuesHolder;import android.animation.ValueAnimator;import android.os.Handler;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.view.ViewGroup;import android.view.animation.OvershootInterpolator;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import com.guoliuya.util.ViewUtil;public class MainActivity extends AppCompatActivity {    private ImageView ivCastle, ivLight1, ivLight2, ivLight3, ivLight4, ivWay1, ivWay2, ivWay3, ivWay4, ivFire1, ivFire2, ivFire3, ivFire4;    private LinearLayout llCastle;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        setView();        findViewById(R.id.click).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                doAnimation();            }        });    }    private void setView() {        ivCastle.setImageResource(R.mipmap.cb);        ivWay1.setImageResource(R.mipmap.f1);        ivWay2.setImageResource(R.mipmap.f2);        ivWay3.setImageResource(R.mipmap.f3);        ivWay4.setImageResource(R.mipmap.f4);        ivFire1.setImageResource(R.mipmap.y1);        ivFire2.setImageResource(R.mipmap.y2);        ivFire3.setImageResource(R.mipmap.y3);        ivFire4.setImageResource(R.mipmap.y4);        ivLight1.setImageResource(R.mipmap.g1);        ivLight2.setImageResource(R.mipmap.g2);        ivLight3.setImageResource(R.mipmap.g3);        ivLight4.setImageResource(R.mipmap.g4);    }    private void initView() {        ivCastle = (ImageView) findViewById(R.id.iv_castle);        llCastle = (LinearLayout) findViewById(R.id.ll_castle);        ivLight1 = (ImageView) findViewById(R.id.iv_light1);        ivLight2 = (ImageView) findViewById(R.id.iv_light2);        ivLight3 = (ImageView) findViewById(R.id.iv_light3);        ivLight4 = (ImageView) findViewById(R.id.iv_light4);        ivWay1 = (ImageView) findViewById(R.id.iv_way1);        ivWay2 = (ImageView) findViewById(R.id.iv_way2);        ivWay3 = (ImageView) findViewById(R.id.iv_way3);        ivWay4 = (ImageView) findViewById(R.id.iv_way4);        ivFire1 = (ImageView) findViewById(R.id.iv_fire1);        ivFire2 = (ImageView) findViewById(R.id.iv_fire2);        ivFire3 = (ImageView) findViewById(R.id.iv_fire3);        ivFire4 = (ImageView) findViewById(R.id.iv_fire4);    }    private void doAnimation() {        Animator lighterAni = getLightersAnimator();        Animator castleAni = getCastleShowingAnimator();        Animator fireworks = getFireworksAnimator();        Animator fireworks2 = getFireworksAnimator();        AnimatorSet set = new AnimatorSet();        set.play(lighterAni).with(castleAni);        set.play(castleAni).after(1100).before(fireworks);        set.play(fireworks).after(1400).before(fireworks2);        set.play(fireworks2).after(2200);        set.start();    }    private Animator getLightersAnimator() {        Animator light1 = getLighterAnimator(ivLight1, -15, 20, -30, 50);        Animator light2 = getLighterAnimator(ivLight2, 0, -50, 30, 30);        Animator light3 = getLighterAnimator(ivLight3, 0, 50, -30, -30);        Animator light4 = getLighterAnimator(ivLight4, -15, -30, 10, -55);        AnimatorSet set = new AnimatorSet();        set.play(light1).with(light4);        set.play(light4);        set.play(light2).with(light3).after(200);        return set;    }    private Animator getLighterAnimator(ImageView lighter, int rotation1, int rotation2, int rotation3,            int rotation4) {        Animator ani1 = getLighterAnimator(lighter, rotation1, rotation2);        Animator ani2 = getLighterAnimator(lighter, rotation2, rotation3);        Animator ani3 = getLighterAnimator(lighter, rotation3, rotation4);        AnimatorSet set = new AnimatorSet();        set.addListener(new ViewShowListener(lighter));        set.play(ani1).before(ani2);        set.play(ani2).before(ani3);        return set;    }    private Animator getLighterAnimator(ImageView lighter, int origin, int destination) {        ObjectAnimator lighterAni = ObjectAnimator.ofFloat(lighter, "rotation", origin, destination);        lighterAni.setDuration(800);        return lighterAni;    }    private Animator getCastleShowingAnimator() {        float castleSY = ViewUtil.getScreenHeight(this) + ViewUtil.dp2Px(this, 240);        float castleDY =                ViewUtil.getScreenHeight(this) - ViewUtil.dp2Px(this, 240) - ViewUtil.dp2Px(this, 30);        PropertyValuesHolder castleX1 = PropertyValuesHolder.ofFloat("y", castleSY, castleDY);        PropertyValuesHolder castleScaleX = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1.0f);        PropertyValuesHolder castleScaleY = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1.0f);        ObjectAnimator castleAni = ObjectAnimator                .ofPropertyValuesHolder(llCastle, castleX1, castleScaleX, castleScaleY);        castleAni.addListener(new ViewShowListener(ivCastle));        castleAni.addListener(new ViewShowListener(llCastle));        castleAni.setInterpolator(new OvershootInterpolator());        castleAni.setDuration(1000);        return castleAni;    }    private Animator getFireworksAnimator() {        Animator fireworkAni1 = getFireWorkAnimator(ivWay1, 155, ivFire1);        Animator fireworkAni2 = getFireWorkAnimator(ivWay2, 162, ivFire2);        Animator fireworkAni3 = getFireWorkAnimator(ivWay3, 157, ivFire3);        Animator fireworkAni4 = getFireWorkAnimator(ivWay4, 111, ivFire4);        AnimatorSet set = new AnimatorSet();        set.play(fireworkAni1).with(fireworkAni2);        set.play(fireworkAni2).after(200);        set.play(fireworkAni4).with(fireworkAni3);        set.play(fireworkAni3).after(200);        return set;    }    private Animator getFireWorkAnimator(final ImageView way, final int wayHeight, final ImageView fire) {        ValueAnimator wayAni = ValueAnimator.ofFloat(0f, ViewUtil.dp2Px(way.getContext(), wayHeight));        wayAni.setDuration(300);        wayAni.addListener(new ViewShowListener(way));        wayAni.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            int lastHeight;            @Override            public void onAnimationUpdate(ValueAnimator valueAnimator) {                Float value = (Float) valueAnimator.getAnimatedValue();                way.getLayoutParams().height = value.intValue();                lastHeight = value.intValue();                way.requestLayout();            }        });        ObjectAnimator wayScaleAni = ObjectAnimator.ofFloat(way, "alpha", 1, 0.2f);        wayScaleAni.setDuration(500);        PropertyValuesHolder alphaValue = PropertyValuesHolder.ofFloat("alpha", 0, 1);        PropertyValuesHolder scaleXValue = PropertyValuesHolder.ofFloat("scaleX", 0, 1);        PropertyValuesHolder scaleYValue = PropertyValuesHolder.ofFloat("scaleY", 0, 1);        ObjectAnimator fireAni = ObjectAnimator                .ofPropertyValuesHolder(fire, alphaValue, scaleXValue, scaleYValue);        fireAni.setDuration(600);        fireAni.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animator) {                fire.setVisibility(View.VISIBLE);                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        way.setVisibility(View.GONE);                    }                }, 300);            }            @Override            public void onAnimationEnd(Animator animator) {                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        fire.setVisibility(View.GONE);                    }                }, 300);            }            @Override            public void onAnimationCancel(Animator animator) {            }            @Override            public void onAnimationRepeat(Animator animator) {            }        });        AnimatorSet set = new AnimatorSet();        set.play(wayAni).with(wayScaleAni);        set.play(wayScaleAni).before(fireAni);        set.play(fireAni);        return set;    }    class ViewShowListener implements Animator.AnimatorListener {        private View view;        public ViewShowListener(View view) {            this.view = view;        }        @Override        public void onAnimationStart(Animator animator) {            view.setVisibility(View.VISIBLE);        }        @Override        public void onAnimationEnd(Animator animator) {        }        @Override        public void onAnimationCancel(Animator animator) {        }        @Override        public void onAnimationRepeat(Animator animator) {        }    }}

demo下载地址:https://github.com/guoliuya/PropertyAnimationDemo
 

    

0 1
原创粉丝点击