Android动画小结

属性动画总结

Property Animation:属性动画是针对于3.0(API 11)以上版本的系统有效;
动画特点:可以设置给任意的Object,包括还没有渲染到屏幕上的对象;
可扩展性:自定义任何类型和属性的动画;

常用的View属性成员:

  • translationX,translationY:控制View的位置,值是相对于View容器左上角坐标的偏移;
  • rotationX,rotationY:控制相当于轴心得旋转;
  • x,y:控制View在在容器中的位置,即左上角坐标加上translationX和translationY的值;
  • alpha:控制View对象的alpha透明度值;

属性动画概述:

/**
 * This is the superclass for classes which provide basic support for animations which can be
 * started, ended, and have AnimatorListeners added to them.
 */
public abstract class Animator implements Cloneable {
    ......
}

所有属性动画的BaseClass就是Animator这个抽象类;

由Animator派生出来的类之间的关系:(我们能看到:AnimatorSet 和ValueAnimator是直接子类,实际我们使用的单个动画效果的时候使用的是ObjectAnimator,ObjectAnimator是ValueAnimator的直接子类,关于ValueAnimator的作用和使用稍后分析,关于TimeAnimator本人使用比较少)
Android动画小结_第1张图片

属性动画的属性:

  • Duration:动画的持续时间;
  • TimeInterpolation:定义动画变化速率的接口,这个接口是所有插值器的规范,像线性插值器、非线性插值器都得实现此接口。
  • TypeEvaluator:用于定义属性值计算方式的接口,有int、float、color类型,根据属性的起始、结束值、和差值一起计算出当前时间的属性值。

以上扯了那么多用的,其实属性动画就是Animator,补间动画就是Animation先记住这么多区别熟练运用之后再去尝试理解它的设计模式,底层实现方式

    /**
     * 旋转
     * @param view
     */
    public void rotateAnimator(View view){
        ObjectAnimator.ofFloat(view,"rotationX",50f,95f)
                .setDuration(3000)
                .start();//x轴的话默认的就是出于0度,往屏幕里面的方向转的

    }

对于ObjectAnimator:提供的几个方法,提供了ofInt、ofFloat、ofObject,这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。当对于属性值,只设置一个的时候,会认为当然对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束~~~

动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法~

所以对于我们自定以的View或者说对于控件的设置某一个属性值的方法中并没有重新绘制View的操作,我们就需要自己写代码去更新视图的操作:

anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        view.invalidate();
                        view.postInvalidate();
                    }
                })

在不是用AnimatorSet的情况下如何设置多个属性同时变化:

public void rotateAnimator(final View view){

        ObjectAnimator anim = ObjectAnimator.ofFloat(view,"abc",1.0f,0f)
                .setDuration(1000);
//这里的abc属性是随便写的 ,因为我们要使用的是ValueAnimator 
//每一个ObjectAnimator都有一个ValueAnimator,很明先就是指值的变化

        anim.start();
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float cVal = (float) valueAnimator.getAnimatedValue();
                view.setAlpha(cVal);
                view.setScaleX(cVal);
                view.setScaleY(cVal);

            }
        });
    }
//从之前的解释我们知道,属性动画是不断的刷新来重新为View设置新的值来达到动画的效果的

实现一个动画更改多个效果:使用propertyValuesHolder

public void rotateAnimator(final View view){

        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha",1f,0f,1f);//这里后面的值是可以赋多个值的,
        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX",1f,0f,1f);
        PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY",1f,0f,1f);
        ObjectAnimator.ofPropertyValuesHolder(view,pvhX,pvhY,pvhZ).setDuration(1000).start();//同理这里的holder中也可以同时放进去多个holder
    }

接下来看一下和ValueAnimator的用法,简单看一下用view垂直移动的动画代码

 public void rotateAnimator(final View view){


        ValueAnimator animator = ValueAnimator.ofFloat(1.0f,0f);
        animator.setTarget(view);
        animator.setDuration(1000);
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                view.setScaleY((Float) valueAnimator.getAnimatedValue());
            }
        });

    }
//这里的和ObjectAnimator的区别就是没有设置属性值,属性值的更改是在监听事件中进行的,所以好处就是我们不需要关心这个属性值是够有get和set方法

TypeEvaluator的用法

public void rotateAnimator(final View view){

        ValueAnimator valueAnimator = new ValueAnimator();
        valueAnimator.setDuration(3000);
        valueAnimator.setObjectValues(new PointF(0,0));
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setEvaluator(new TypeEvaluator(){

            @Override
            public PointF evaluate(float v, PointF pointF, PointF t1) {
                Log.i("TAG",200 * v * 3+"");
                PointF point = new PointF();
                point.x = 200 * v * 3;//此时x的值就是在0-600之间均匀的增加的  至于速率是多少我也暂时不知道
                point.y = 0.5f * 200 * (v * 3) * (v * 3);//这里的v就相当于是自变量x从0-1变化所以y的值就类似于900*x的平方
                return point;
            }
        });

        valueAnimator.start();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                PointF point = (PointF) valueAnimator.getAnimatedValue();
                //这里可以获取得到PointF中的值x和y对要操作的控件进行赋值操作了

            }
        });

    }

  //这里我们为什么要使用这种方式也很明显 因为x和y的值要同时变化,ofInt和ofFloat就不能使用,所以我们自定义一种TypeEvaluator,如果两个参数仍然不够使用的话我们可以自定义一个Bean作为泛型传递进去就ok

动画的监听事件:

ObjectAnimator animator = ObjectAnimator.ofFloat(view,"alpha",0.5f);//只有一个参数的就是代表初始值
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                //动画开始的时候
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //动画结束的时候
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                //动画取消
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                //动画取消的
            }
        });

  //对于之上的方法太长 要复写所有的方法,我们可以使用AnimatorListenerAdapter 去实现其中的一个方法就可以

AnimatorSet的使用

  ObjectAnimator animator1 = ObjectAnimator.ofFloat(view,"alpha",0.5f);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(view,"translateX",0.5f);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(animator1,animator2);
        //或者
        animatorSet.play(animator1).with(animator2);
        animatorSet.setDuration(1000);
        animatorSet.setInterpolator(new LinearInterpolator());
        animatorSet.start();

以上是使用代码来实现动画,下面是使用xml文件加载出来动画

1.文件路径 res/animator/动画xml文件

先看一个最简单的:


<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="scaleX"
    android:valueFrom="1.0"
    android:valueTo="2.0"
    android:valueType="floatType">
objectAnimator>


 Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scalex);
        animator.setTarget(view);
        animator.start();

动画集合的xml实现


<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">
    <objectAnimator
        android:duration="1000"
        android:propertyName="scaleX"
        android:valueFrom="1.0"
        android:valueTo="0.5"
        android:valueType="floatType"/>

    <objectAnimator
        android:duration="1000"
        android:propertyName="scaleY"
        android:valueFrom="1.0"
        android:valueTo="0.5"
        android:valueType="floatType"/>

set>

在Activity中的实现了上面没什么区别

  Animator animator = AnimatorInflater.loadAnimator(this, R.animator.myset);
        animator.setTarget(view);
        animator.start();

关于布局动画的实现方式:主要是通过LayoutTransition来实现

  1. APPEARING ——元素在容器中显示时需要动画显示
  2. CHANGE_APPEARING——由于容器中要显示一个新的元素,其他元素的变化需要动画显示
  3. DISAPPERARING——元素在容器中消失时需要动画
  4. CHANGE_DISAPPERARING——由于容器中某个元素要消失,其他元素的变化需要动画显示

一个布局动画的Demo提供参考

package com.example.administrator.animatortest;

import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.Keyframe;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.graphics.Color;
import android.graphics.PointF;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private Button mButtonAdd;
    private Button mButtonReset;
    private GridLayout mGridLayout;

    private int buttonNumbers = 1;

    private LayoutTransition mLayoutTransition;

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

        mButtonAdd = (Button) findViewById(R.id.layout_animator_addbutton);
        mButtonAdd.setOnClickListener(this);

        mButtonReset = (Button) findViewById(R.id.layout_animator_resetbutton);
        mButtonReset.setOnClickListener(this);

        mGridLayout = (GridLayout) findViewById(R.id.layout_animator_gridview);

        //为GridView设置LayoutTransition对象
        mLayoutTransition = new LayoutTransition();
        mGridLayout.setLayoutTransition(mLayoutTransition);

        mLayoutTransition.setDuration(300);
        mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING,30);
        mLayoutTransition.setStagger(LayoutTransition.CHANGE_DISAPPEARING,30);
        //和setStagger的区别就是setStagger是通过第一个参数来作为条件单独为这种情况下设置动画时间
        //所以duration是要放在前面的说  不然放在后面 就把之前的属性重新覆盖了

        //初始化自定义的动画效果
        customLayoutTransition();


    }

    /**
     * 自定义效果
     */
    private void customLayoutTransition() {

        //这里的话是为出现的View设置的动画效果
        ObjectAnimator mAnimatorAppearing = ObjectAnimator.
                ofFloat(null,"rotationY",90.0f,0.0f)
                .setDuration(mLayoutTransition.getDuration(LayoutTransition.APPEARING));//添加的动画时间

        //为LayoutTransition设置动画和类型
        mLayoutTransition.setAnimator(LayoutTransition.APPEARING,mAnimatorAppearing);

        /**
         * Add Button
         * LayoutTransition.CHANGE_APPEARING
         * 当增加一个Button时,设置其他Button的动画效果
         */

        PropertyValuesHolder pvhLeft =
                PropertyValuesHolder.ofInt("left", 0, 1);
        PropertyValuesHolder pvhTop =
                PropertyValuesHolder.ofInt("top", 0, 1);
        PropertyValuesHolder pvhRight =
                PropertyValuesHolder.ofInt("right", 0, 1);
        PropertyValuesHolder pvhBottom =
                PropertyValuesHolder.ofInt("bottom", 0, 1);

        PropertyValuesHolder mHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f,0.0f,1.0f);
        PropertyValuesHolder mHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f,0.0f,1.0f);
        ObjectAnimator mObjectAnimatorChangeAppearing = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,
                pvhTop,pvhRight,pvhBottom,mHolderScaleX,mHolderScaleY).setDuration(mLayoutTransition
                .getDuration(LayoutTransition.CHANGE_APPEARING));
        mLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, mObjectAnimatorChangeAppearing);
        mObjectAnimatorChangeAppearing.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                // TODO Auto-generated method stub
                super.onAnimationEnd(animation);
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setScaleX(1f);
                view.setScaleY(1f);
            }
        });

        /**
         * Delete Button
         * LayoutTransition.DISAPPEARING
         * 当删除一个Button时,设置该Button的动画效果
         */
        ObjectAnimator mObjectAnimatorDisAppearing = ObjectAnimator.ofFloat(null, "rotationX", 0.0f,90.0f)
                .setDuration(mLayoutTransition.getDuration(LayoutTransition.DISAPPEARING));
        mLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, mObjectAnimatorDisAppearing);
        mObjectAnimatorDisAppearing.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                // TODO Auto-generated method stub
                super.onAnimationEnd(animation);
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setRotationX(0.0f);
            }
        });

        /**
         * Delete Button
         * LayoutTransition.CHANGE_DISAPPEARING
         * 当删除一个Button时,设置其它Button的动画效果
         */
        //Keyframe 对象中包含了一个时间/属性值的键值对,用于定义某个时刻的动画状态。
        Keyframe mKeyframeStart = Keyframe.ofFloat(0.0f, 0.0f);
        Keyframe mKeyframeMiddle = Keyframe.ofFloat(0.5f, 180.0f);
        Keyframe mKeyframeEndBefore = Keyframe.ofFloat(0.999f, 360.0f);
        Keyframe mKeyframeEnd = Keyframe.ofFloat(1.0f, 0.0f);

        PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",
                mKeyframeStart,mKeyframeMiddle,mKeyframeEndBefore,mKeyframeEnd);
        ObjectAnimator mObjectAnimatorChangeDisAppearing = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,pvhTop,pvhRight,pvhBottom,mPropertyValuesHolder)
                .setDuration(mLayoutTransition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        mLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, mObjectAnimatorChangeDisAppearing);
        mObjectAnimatorChangeDisAppearing.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                // TODO Auto-generated method stub
                super.onAnimationEnd(animation);
                View view = (View) ((ObjectAnimator) animation).getTarget();
                view.setRotation(0.0f);
            }
        });



    }

    /**
     * 点击事件
     * @param v
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.layout_animator_addbutton:
                addButton();
                break;
            case R.id.layout_animator_resetbutton:
                resetButton();
                break;
            default:
                break;
        }
    }

    /**
     * 删除所有的View重置GridLayout
     */
    private void resetButton() {
        mGridLayout.removeAllViews();
        buttonNumbers=1;
    }

    /**
     * 增加一个Button
     */
    private void addButton() {
        Button mButton =  new Button(this);
        LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(50,50);
        mButton.setLayoutParams(mLayoutParams);
        mButton.setText(String.valueOf(buttonNumbers++));
        mButton.setTextColor(Color.rgb(0,0,0));

        if(buttonNumbers % 2 ==1){
            mButton.setBackgroundColor(Color.rgb(45,118,87));
        }else {
            mButton.setBackgroundColor(Color.rgb(225,24,0));
        }

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mGridLayout.removeView(v);
            }
        });

        mGridLayout.addView(mButton,Math.min(1,mGridLayout.getChildCount()));//这里使用的是插入的方法 所以添加的话也会导致 其他view的变化

    }

}

布局文件


这个Dmeo 基本上涵盖了属性动画的用法和布局动画的用法,手动敲一下还是能更深入的理解的

你可能感兴趣的:(android)