Android 动画详解-思维导图版

转载请注明链接: https://blog.csdn.net/feather_wch/article/details/81503233

涉及视图动画(补间动画、逐帧动画)、属性动画三种动画的使用方法,包括XML形式和代码形式,解析动画的底层原理和注意点。

Android 动画详解-思维导图版(82题)

版本:2018/08/15-1(17:19)


动画的分类(3题)

1、Android动画分为两种:

  1. View动画(Animation)
  2. 属性动画

2、View动画和属性动画的优缺点

  1. View动画缺点显著:不具备交互性,只能做普通的动画效果
  2. View动画优点:效率高、使用方便
  3. 属性动画优点:具有交互性

3、View动画和属性动画区别

  1. View动画并不支持对控件宽高做动画, 即使进行放大,本质控件的文字等也会被拉伸
  2. 属性动画就可以给任意属性做动画

视图动画(21题)

1、View动画

  • 提供AlphaAnimation、RotateAnimation、TranslateAnimation、ScaleAnimation四种动画方式
  • 提供动画合集AnimationSet,用于动画混合
  • 缺点显著:不具备交互性,只能做普通的动画效果
  • 优点:效率高、使用方便

视图动画分类

2、视图动画分为两种

  1. 补间动画
  2. 逐帧动画

3、 View动画的使用

  1. XML定义动画
  2. 代码动态创建
  3. XML形式的View动画,需要在res/anim/目录下创建XML文件custom.xml

补间动画

4、什么是补间动画

  1. 通过确定开始和结束的视图样式,中间动画变化过程由系统补全。

5、 补间动画的分类

分类 XML标签 效果
TranslateAnimation translate 移动View
ScaleAnimation scale 放大或缩小View
RotateAnimation rotate 旋转View
AlphaAnimation alpha 改变透明度

XML形式

6、 View动画如何通过XML定义?各属性要的作用?

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true"
    android:duration="2000" //持续时间
    android:fillAfter="true"> //动画后是否停留在结束位置
    <alpha
        android:fillAfter="true"
        android:dutaion="1000"  //动画持续时间
        android:fromAlpha="0.1" //初始透明度,1为不透明,0为完全透明
        android:toAlpha="1"/>

    <scale
        android:fromXScale="0.5" //水平方向缩放,从0.5放大至1.2
        android:toXScale="1.2"
        android:fromYScale="1.1" //垂直方向缩放,从1.1缩小至0.3
        android:toYScale="0.3"
        android:pivotX="0.5"     //轴点(X,Y)
        android:pivotY="0.6"/>

    <translate
        android:fromXDelta="10" //x的初始值
        android:toXDelta="120"  //x的结束值
        android:fromYDelta="0"
        android:toYDelta="100"/>

    <rotate
        android:dutaion="1000"
        android:fromDegrees="0" //旋转开始的角度
        android:toDegrees="180" //旋转结束的角度
        android:pivotY="0"   //根据轴点进行旋转
        android:pivotX="0"/>

</set>
  1. android:duration表示持续时间,set有duration属性,内部动画的duration全部以set的为准
  2. android:fillAfter动画结束后,是否留在结束位置
  3. set标签没有duration时,内部的各种动画标签均以自身的duration为准
  4. scale中的(pivotX,pivotY)是以该点坐标为中心进行缩放。无论坐标超过View本身的范围。
  5. rotate中的(pivotX,pivotY)是旋转的中心坐标,以此点进行旋转。

7、如何使用XML定义的动画

val imageview = findViewById<ImageView>(R.id.imaview)
val animation = AnimationUtils.loadAnimation(this, R.anim.custom_animation)
imageview.startAnimation(animation)

代码形式

透明度动画

8、透明度动画

        //透明度
        AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
        alphaAnimation.setDuration(2000);
        imageView1.setAnimation(alphaAnimation);
旋转动画

9、旋转动画

        //旋转
        RotateAnimation rotateAnimation = new RotateAnimation(0, 10);
        rotateAnimation.setDuration(2000);
        imageView2.setAnimation(rotateAnimation);
缩放动画

10、缩放动画

        //缩放
        ScaleAnimation scaleAnimation = new ScaleAnimation(0.5f, 1, 0.5f, 1);
        scaleAnimation.setDuration(2000);
        imageView3.setAnimation(scaleAnimation);
平移动画

11、平移动画

        //平移
        TranslateAnimation translateAnimation = new TranslateAnimation(0, 50, 0, 50);
        translateAnimation.setDuration(2000);
        imageView4.setAnimation(translateAnimation);
动画集合(AnimationSet)

12、动画集合

        /*
        *  动画集合,可以混合多种动画效果。
        * */
        AnimationSet animationSet = new AnimationSet(true);
        animationSet.setDuration(3000);
        animationSet.addAnimation(alphaAnimation);
        animationSet.addAnimation(rotateAnimation);
        imageView.setAnimation(animationSet);

逐帧动画

13、 逐帧动画是什么?注意点?

  1. 逐帧动画与补间动画区别在于,需要定义一帧帧的动画内容。
  2. 补间动画对应类是xxxAnimation,而逐帧动画对应类是AnimationDrawable
  3. 帧动画采用的标签animation-list,内部是item标签。
  4. 帧动画有可能出现OOM,因此图片不能太大

自定义视图动画

14、如何自定义View动画

  1. 自定义动画继承Animation
  2. 重写initialize-做一些初始化操作
  3. 重写applyTransformation-进行一定的矩阵变换即可,通常通过Camera简化矩阵转换过程

特殊使用场景

Acitivity切换动画

15、Acitivity切换动画

  1. 在Acitivty中调用overridePendingTransition
  2. overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim)第一个参数,为新Acitivty进入时动画。第二个参数,为旧acitivty退出时动画。
  3. 必须紧挨着startActivity()或者finish()函数之后调用

Fragment切换动画

16、Fragment的切换动画

  1. 通过FragmentTransactionsetCustomAnimations()方法设置
  2. 必须是View动画

布局动画

17、什么是布局动画

  1. 通过给ViewGroup设置布局动画,达到View逐渐呈现的过渡效果。
  2. android:animateLayoutChanges="true"者可以给布局添加系统默认的效果。
  3. 如果要自定义过渡效果,需要通过LayoutAnimationController类来自定义,本质是给布局一个视图动画,在View出现时产生过渡效果。
代码形式

18、布局动画实例

// 1. 过渡动画
ScaleAnimation sa = new ScaleAnimation(0,1,0,1);
 sa.setDuration(1000);
// 2. 设置布局动画的显示属性(第一个参数,是需要作用的动画,而第二个参数,则是每个子View显示的delay时间)
LayoutAnimationController lac = new LayoutAnimationController(sa,0.5f);     
// 3. 子View的显示顺序(delay > 0)
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
// 4. 为ViewGroup设置布局动画
LinearLayout mLinear  = (LinearLayout) findViewById(R.id.mLinear);
mLinear.setLayoutAnimation(lac);

19、布局动画的子View显示顺序

当delay的时间不为0时,可以设置子View显示的顺序:
1. LayoutAnimationController.ORDER_NORMAL——顺序
1. LayoutAnimationController.ORDER_RANDOM——随机
1. LayoutAnimationController.ORDER_REVERSE——反序

XML形式

20、XML形式的LayoutAnimation使用步骤

1-定义布局动画,使用layoutAnimation标签, 并引用item动画

//布局动画:res/anim/layout_animation
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.3"
    android:animationOrder="normal"
    android:animation="@anim/item_animation"/>

2-定义item动画(和一般View动画一样定义)

//item动画:res/anim/item_animation
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true"
    android:duration="300"
    android:fillBefore="false">
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"/>
    <translate
        android:fromXDelta="500"
        android:toXDelta="0"/>
</set>
  1. ViewGroup的对象使用布局动画- android:layoutAnimation="@anim/layout_animation"
//ListView中使用
<ListView
    android:id="@+id/listview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layoutAnimation="@anim/layout_animation">
</ListView>

21、XML形式LayoutAnimation要点

  1. android:delay="0.3"是指子元素动画延时开始的间隔。比如item动画时间为200ms,则每个子元素动画开始的间隔就是60ms.
  2. android:animationOrder="normal"动画的顺序:顺序、逆序和随机
  3. android:animation="@anim/item_animation"引用子元素所采用的动画

属性动画(47题)

1、属性动画是什么?

  1. API11提出的新特性
  2. 通过对属性操作能对任何对象做动画。
  3. 支持更多的动画效果
  4. 属性动画包括ObjectAnimator、AnimatorSet、ValueAnimator、Interpolator
  5. ObjectAnimator控制一个对象的一个属性
  6. AnimatorSet是将多个ObjectAnimator组合并形成动画。
    5, ObjectAnimator继承自ValueAnimator

2、属性值有哪些?

属性值 作用
translationX、translationY 控制View从左上角偏移的位置
rotation、rotationX、rotationY 控制View围绕支点做2D和3D旋转
scaleX、scaleY 围绕支点2D缩放
pivotX、pivotY 控制支点位置,默认为View中心
x、y 描述View的最终位置
alpha 透明度,默认1不透明,0为完全透明

ObjectAnimator

3、ObjectAnimator的使用步骤

  1. 如果是自定义控件,需要添加 setter / getter 方法;
  2. ObjectAnimator.ofXXX() 创建 ObjectAnimator 对象;
  3. start() 方法执行动画。
public class SportsView extends View {float progress = 0;
    // 创建 getter 方法
    public float getProgress() {
        return progress;
    }
    // 创建 setter 方法
    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }
    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint);
    }
}
// 创建 ObjectAnimator 对象
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65);  
// 执行动画
animator.start();  

实例:移动

4、属性动画实例:移动

//X轴平移一定距离
ObjectAnimator.ofFloat(imageView, "translationX", 100f).start()

实例:背景颜色变化

5、属性动画实例:背景颜色变化

val colorAnim = ObjectAnimator.ofInt(imageview, "backgroundColor", -0x7f80, -0x7f7f01)
colorAnim.setDuration(1000)
colorAnim.setEvaluator(ArgbEvaluator())
colorAnim.repeatCount = ValueAnimator.INFINITE
colorAnim.repeatMode = ValueAnimator.REVERSE
colorAnim.start()

ObjectAnimator的API

6、ObjectAnimator的通用方法

setDuration(int duration) //设置动画时长-单位是毫秒。

ObjectAnimator animator = ObjectAnimator.ofFloat(imageView2, "translationX", 500);
//1. 设置动画时长,单位毫秒。
animator.setDuration(2000);
//2. 设置插值器(动画的速度和表现形式)
animator.setInterpolator(new AccelerateDecelerateInterpolator()); //先加速,再减速。
animator.setInterpolator(new LinearInterpolator()); //匀速
animator.setInterpolator(new AccelerateInterpolator()); //加速
animator.setInterpolator(new DecelerateInterpolator()); //减速
animator.setInterpolator(new AnticipateInterpolator()); //先回拉再进行正常动画(如放大的会先缩小在放大)
animator.setInterpolator(new OvershootInterpolator()); //会超过目标值,然后回到目标值。
animator.setInterpolator(new AnticipateOvershootInterpolator()); //先回拉,正常动画,会超过目标值然后反弹到目标值。
animator.setInterpolator(new BounceInterpolator()); //目标处弹动
animator.setInterpolator(new CycleInterpolator(0.5f)); //一个正弦/余弦曲线,可以自定义曲线的周期,动画可以不到终点就结束,可以到达终点后回弹。
/**
 * 自定义动画完成度 / 时间完成度曲线。
 * 1. path-必须连续不能间断,也不能重叠
 * https://ws4.sinaimg.cn/large/006tKfTcly1fj8jmom7kaj30cd0ay74f.jpg
 * */
Path interpolatorPath = new Path();
// 先以「动画完成度 : 时间完成度 = 1 : 1」的速度匀速运行 25%
interpolatorPath.lineTo(0.25f, 0.25f);
// 然后瞬间跳跃到 150% 的动画完成度
interpolatorPath.moveTo(0.25f, 1.5f);
// 再匀速倒车,返回到目标点
interpolatorPath.lineTo(1, 1);
animator.setInterpolator(new PathInterpolator(interpolatorPath));

animator.setInterpolator(new FastOutLinearInInterpolator()); //加速运动(贝塞尔曲线)
animator.setInterpolator(new FastOutSlowInInterpolator());  //先加速再减速
animator.setInterpolator(new LinearOutSlowInInterpolator()); //持续减速
animator.start();

属性动画要点

7、属性动画要点

  1. 属性动画要求该属性必须要有set/get方法
  2. 插值器和估值器都可以自定义
  3. 插值器自定义需要实现Interpolator或者TimeInterpolator
  4. 估值器自定义需要实现TypeEvaluator接口
  5. int/float/Color以外的类型必须要自定义类型估值算法

8、属性动画想要生效,必须满足两个条件

  1. 该属性需要有setget方法
  2. set方法所做出的属性改变必须能通过UI等改变反映出来(Button的setWidth方法本质就不能改变空间的高度)

9、TextView/Button改变宽高的动画为什么不能生效?

  1. TextView以及子类的确有getWidth/setWidth方法,满足条件1,不满足条件2
  2. 源码中getWidth=mRight-mLeft的确是View的高度android:layout_width,该条满足条件1
  3. setWidth设置的是TextView的最大宽度和最小宽度,对应着android:width属性,并不是设置View的宽度,因此不满足条件2

10、官方针对属性动画生效的条件问题,提供三种解决办法

  1. 有权限的情况下,给对象加上get/set方法————一般难以做到,因为无权给SDK内部实现添加方法
  2. 使用装饰者模式包装原始对象,间接为其提供get/set方法
  3. 采用ValueAnimator,监听动画过程,自己实现属性的改变

11、装饰者模式获得get、set

1-实现包装类

    private class WrapperView{
        private View view;
        public WrapperView(View view){
            this.view = view;
        }
        public int getWidth(){
            return view.getLayoutParams().width;
        }
        public void setWidth(int width){
            view.getLayoutParams().width = width;
            view.requestLayout();
        }
    }

2-使用包装类实现属性动画

WrapperView wrapperView = new WrapperView(imageView);
ObjectAnimator.ofInt(wrapperView, "width", 500).setDuration(2000).start();

XML形式

12、通过xml使用ObjectAnimator

// res/animator/scalex.xml(在X轴上缩放,从1.0>0.5)
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="scaleX"
    android:valueFrom="1.0"
    android:valueTo="0.5"
    android:valueType="floatType">

</objectAnimator>

13、Java中使用该动画

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

监听器

14、ViewPropertyAnimator/ObjectAnimator设置AnimatorListener监听器

//1-设置方法
view.animate().setListener(xxx);
objectAnimator.addListener(xxx);
//2-监听器的回调
//监听全部步骤
new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        //动画开始执行时调用
    }

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

    @Override
    public void onAnimationCancel(Animator animation) {
        //1-动画通过cancel取消时,调用
        //2-cancel()之后onAnimationEnd()依旧会调用
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
        //ViewPropertyAnimator不支持重复,因此该方法无效
        //ObjectAnimator通过setRepeatMode()和setRepeatCount()或者repeat()重复执行时,会调用
    }
}

//选择性监听
objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
            }
        });

15、ViewPropertyAnimator/ObjectAnimator设置AnimatorUpdateListener

//1-设置更新监听器
objectAnimator.addUpdateListener(xxx);
view.animate().setUpdateListener(xxx);
//2-回调方法
new ValueAnimator.AnimatorUpdateListener(){
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        //1-当动画的属性更新时,就会调用
        //2-参数的ValueAnimator是ObjectAnimator的父类,也是ViewPropertyAnimator的内部实现
        //3-ValueAnimator具有很多方法(查看当前的动画完成度、当前属性等)
    }
}

16、ViewPropertyAnimator/ObjectAnimator设置AnimatorPauseListener

//1-设置暂停监听器
View.animate().setUpdateListener(xxx);
objectAnimator.addPauseListener(new Animator.AnimatorPauseListener() {
    @Override
    public void onAnimationPause(Animator animation) {

    }

    @Override
    public void onAnimationResume(Animator animation) {

    }
});

AnimatorSet

17、AnimatorSet的作用和使用

  1. 将动画融合(类似PropertyValuesHolder)
  2. 在此基础上还可以控制动画的顺序。
        ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView,"translationY", 300);
        ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView,"scaleX", 1f, 0, 1f);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(objectAnimator1, objectAnimator2);
        animatorSet.setDuration(2000);
        animatorSet.start();

XML形式

18、XML中定义属性动画集

需要在res文件夹中创建animator文件夹, 并创建XML文件

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together" //表明动画集合中子动画是同时播放还是顺序播放
    >

    <objectAnimator  //对应ObjectAnimator
        android:propertyName="translationX" //属性名称
        android:duration="1000"        //持续时间
        android:valueFrom="200"        //属性起始值
        android:valueTo="500"          //属性结束值
        android:startOffset="10"       //动画的延迟时间,动画开始后,需要多少ms才真正播放动画
        android:repeatCount="10"       //动画的重复次数,默认0-1为无限循环
        android:repeatMode="restart"   //动画的重复模式
        android:valueType="intType"    //表示propertyName所指属性的类型,但当属性表示颜色时不需要指定valueType
        />

    <animator //对应ValueAnimator
          //相比于objectAnimator缺少一个android:propertyName
        />

    <set> //对应set
        ...
    </set>
</set>

19、代码中使用XML中定义的属性动画(包括AnimatorSet)

val set = AnimatorInflater.loadAnimator(this, R.animator.animator) as AnimatorSet
set.setTarget(listView)
set.start()

PropertyValuesHolder

20、PropertyValueHolder的作用

  1. ViewPropertyAnimator中通过链式调用就可以同时改变多个属性
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);  
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);  
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)  
animator.start();  

21、PropertyValuesHolder实现动画效果

类似于AnimationSet的作用,将多种效果共同作用于对象。

PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationY", 200);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(imageView, pvh1, pvh2, pvh3).setDuration(1000).start();

22、ObjectAnimator.ofPropertyValuesHolder()解析

public static ObjectAnimator ofPropertyValuesHolder(Object target,
        PropertyValuesHolder... values) {
    //1. 本质是创建ObjectAnimator对象,并将`PropertyValuesHolder`存入
    ObjectAnimator anim = new ObjectAnimator();
    anim.setTarget(target);
    anim.setValues(values);
    return anim;
}
  1. 本质是创建ObjectAnimator对象,并将PropertyValuesHolder存入
  2. ObjectAnimator.start()方法最底层本质就是通过PropertyValuesHoldersetupValue调用get方法,setAnimatedValue方法set属性值

ofKeyframe

23、PropertyValuesHolders.ofKeyframe()

  1. 把一个属性进行拆分
// 1、在 0% 处开始
Keyframe keyframe1 = Keyframe.ofFloat(0, 0);  
// 2、时间经过 50% 的时候,动画完成度 100%
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100);  
// 3、时间见过 100% 的时候,动画完成度倒退到 80%,即反弹 20%
Keyframe keyframe3 = Keyframe.ofFloat(1, 80);  
// 4、合成
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);
// 5、使用
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder);  
animator.start();  

ViewPropertyAnimator

24、ViewPropertyAnimator的由来

  • 属性动画提供的ValueAnimator类和ObjectAnimator类本质不是针对View对象而设计的,而是一种对数值不断操作的过程,但大部分情况下还是对View进行动画操作的。因此Google官方3.1中推出了ViewPropertyAnimator

25、ViewPropertyAnimator的特点?

  1. 专门针对View对象动画而操作的类。
  2. 提供了更简洁的链式调用设置多个属性动画,这些动画可以同时进行。
  3. 拥有更好的性能,多个属性动画是一次同时变化,只执行一次UI刷新(也就是只调用一次invalidate,而n个ObjectAnimator就会进行n次属性变化,就有n次invalidate)。
  4. 每个属性提供两种类型方法设置(直接设置和By的形式)。
  5. 该类只能通过View的animate()获取其实例对象的引用

26、ViewPropertyAnimator和ObjectAnimator的区别

  1. 拥有更好的性能,多个属性动画是一次同时变化,只执行一次UI刷新(也就是只调用一次invalidate)
  2. 多个属性动画,也就是n个ObjectAnimator就会进行n次属性变化,就有n次invalidate。

27、ViewPropertyAnimator的使用方法

1-只能通过view.animate()方法实例化
2-view.animate().translationX(500);
ViewPropertyAnimator
3-图中大部分方法都具有xxxBy()版本,如view.animate().translationXBy(10);会在当前基础上+10

28、View的animate()实现动画效果

  1. 是属性动画的一种简写形式。
        imageView.animate() //获得animator
                .alpha(0)
                .y(300)
                .setDuration(3000)
                .withStartAction(new Runnable() {
                    @Override
                    public void run() {
                    }
                })
                .withEndAction(new Runnable() {
                    @Override
                    public void run() {
                        //结束动作后,在UI线程操作
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                            }
                        });
                    }
                })
                .start(); //开始

29、 ViewPropertyAnimator.withStartAction/EndAction()

这两个方法是 ViewPropertyAnimator 的独有方法。

30、withStartAction/EndAction()和set/addListener()中onAnimationStart() / onAnimationEnd() 的区别

  1. withStartAction() / withEndAction() 是一次性的,在动画执行结束后会自动弃掉,之后再重用 ViewPropertyAnimator 来做别的动画,用它们设置的回调也不会再被调用。
  2. set/addListener() 所设置的 AnimatorListener持续有效的,当动画重复执行时,回调总会被调用。
  3. withEndAction()设置的回调只有在动画正常结束时才会被调用,而在动画被取消时不会被执行。这点和 AnimatorListener.onAnimationEnd() 的行为不一致。

ValueAnimator

31、ValueAnimator的作用

1.ObjectAnimator的父类,
2. 提供数值变化和监听,本身不完成动画,通过得到的数值可以去进行一定变换。

32、ValueAnimator的使用: Int类型属性

1、Int属性(直接获取数值 or 通过插值器和估值器获得最终数据)

//起始值和终止值是0~100
ValueAnimatorvalueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.addUpdateListener(
       new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animator) {

       //通过getAnimatedValue()来获取计算出的值
       //因为上面是ofInt,所以这里可以强转为Integer
       Integer animatedValue = (Integer) animator.getAnimatedValue();
       ((TextView) view).setText("$ " + animatedValue);
    }
});
valueAnimator.setDuration(3000);
valueAnimator.start();

//等同于
ValueAnimatorvalueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.addUpdateListener(
       new ValueAnimator.AnimatorUpdateListener() {

           private IntEvaluator mEvaluator=new IntEvaluator();

           @Override
           public void onAnimationUpdate(ValueAnimator animator) {

               //先得到插值器返回的数据变化的百分比
               float animatedFraction =animator.getAnimatedFraction();

               //使用估值器,通过上面的 数据变化的百分比,得到改变后的数据
               Integer evaluate = mEvaluator.evaluate(animatedFraction, 0, 100);
               ((TextView) view).setText("$ " + evaluate);
           }
       });
valueAnimator.setDuration(3000);
valueAnimator.start();

33、ValueAnimator的使用: 颜色类型属性

//Argb传递的值也是int
ValueAnimatorvalueAnimator = ValueAnimator.ofArgb(/*RED*/0xFFFF8080, /*BLUE*/0xFF8080FF);
valueAnimator.addUpdateListener(
       new ValueAnimator.AnimatorUpdateListener() {
           @Override
           public void onAnimationUpdate(ValueAnimator animator) {

               int animatedValue = (int) animator.getAnimatedValue();
               view.setBackgroundColor(animatedValue);

           }
       });
valueAnimator.setDuration(3000);
valueAnimator.start();

//等同于
ValueAnimatorvalueAnimator = ValueAnimator.ofArgb(/*RED*/0xFFFF8080, /*BLUE*/0xFF8080FF);
valueAnimator.addUpdateListener(
       new ValueAnimator.AnimatorUpdateListener() {

           private ArgbEvaluator mEvaluator=new ArgbEvaluator();

           @Override
           public void onAnimationUpdate(ValueAnimator animator) {
               float animatedFraction =animator.getAnimatedFraction();
               Object evaluate = mEvaluator.evaluate(animatedFraction, /*RED*/0xFFFF8080, /*BLUE*/0xFF8080FF);
               view.setBackgroundColor((int)evaluate);

           }
       });
valueAnimator.setDuration(3000);
valueAnimator.start();

xml形式

34、xml形式使用ValueAnimator

// res/animator/anim_test.xml
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueType="intType"
    android:valueFrom="0"
    android:valueTo="100"
    android:repeatCount="1"
    android:repeatMode="restart"
    android:startOffset="1000"
    android:duration="3000">
</animator>
ValueAnimator valueAnimator= (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.anim_test);
valueAnimator.addUpdateListener(
        new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animator) {
                int animatedValue = (int) animator.getAnimatedValue();
                ((TextView) view).setText("$ " + animatedValue);
            }
        });
valueAnimator.start();

监听器

35、监听器的使用

valueAnimator.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) {
    }
});

Interpolator

3、Interpolator作用

  1. 插值器可以定义动画的变换速率(根据时间流逝的百分比来计算出当前属性值改变的百分比)
  2. TimeInterpolator: 插值器,时间插值器,用于决定动画运动的变化曲线,可以实现如加速、减速、弹性动画等效果。
  3. 系统预置了:LinearInterpolator(匀速动画)、AccelerateDecelerateInterpolator(动画两头慢中间快)、DecelerateInterpolator(动画越来越慢)等等

37、自定义Interpolator

1、自定义一个弹性插值器

public class SpringInterpolator implements Interpolator{
    //弹性因数
    private float factor;

    public SpringInterpolator(float factor) {
        this.factor = factor;
    }

    @Override
    public float getInterpolation(float input) {
        return (float) (Math.pow(2, -10 * input) * Math.sin((input - factor / 4) * (2 * Math.PI) / factor) + 1);
    }
}

2、使用

valueAnimator.setInterpolator(new SpringInterpolator(0.4f));

PathInterpolator

38、PathInterpolator的构造方法

方法 作用
PathInterpolator(Path path) 用Path创建Interpolator
PathInterpolator(float controlX1, float controlY1, float controlX2, float controlY2) 用三次贝塞尔曲线创建Interpolator。
PathInterpolator(float controlX, float controlY) 用二次贝塞尔曲线创建Interpolator。

39、二次贝塞尔曲线构造PathInterpolator

起点和终点是指定的(0f, 0f)和(1f, 1f), 唯一能指定的就是控制点

PathInterpolator pathInterpolator
= new PathInterpolator(0.5f, 1f);

40、三次贝塞尔曲线构造PathInterpolator

起点和终点是指定的(0f, 0f)和(1f, 1f), 能指定两个控制点

PathInterpolator pathInterpolator = new PathInterpolator(0.5f, 1f, 2f, 2f);

41、Path构造PathInterpolator

  1. path构造插值器,能通过贝塞尔曲线等方法进行构造。
  2. 注意:禁止x值一定时,有两个y值
Path path = new Path();
path.moveTo(0f,0f);
path.lineTo(1f,0.5f);
path.lineTo(1f,1f);

TypeEvaluator

42、TypeEvaluator的作用

  1. TypeEvaluator:类型估值算法,也称为估值器
  2. 作用: 根据当前属性改变的百分比来计算改变后的属性值
  3. 系统预置:IntEvaluator(针对整型属性)、FloatEvaluator(针对浮点型)、ArgbEvaluator(针对Color属性)
  4. TimeInterpolator和TypeEvaluator是实现非匀速动画的重要手段。

43、ArgbEvaluator颜色估值器

//1、颜色的渐变
animator = ObjectAnimator.ofInt(this, "color", 0xffff0000, 0xff00ff00);
animator.setEvaluator(new ArgbEvaluator());
//2、颜色渐变(API >= 21)
animator = ObjectAnimator.ofArgb(this, "color", 0xffff0000, 0xff00ff00);

animator.start();

44、TypeEvaluator使用实例:自定义点估值器

1、自定义估值器

public class PointEvaluator implements TypeEvaluator<Point>{
    @Override
    public Point evaluate(float fraction, Point startValue, Point endValue) {
        float x = startValue.x + fraction * (endValue.x - startValue.x);
        float y = startValue.y + fraction * (endValue.y - startValue.y);
        Point point = new Point((int)x, (int)y);
        return point;
    }
}

2、ValueAnimator的ofObject进行动画

ValueAnimatorvalueAnimator=ValueAnimator.ofObject(new PointEvaluator(), new Point(0, 0), new Point(100, 200));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
       Point point = (Point) animation.getAnimatedValue();
       xxx获得到变化后的点xxx
    }
});

valueAnimator.setDuration(3000);
valueAnimator.start();

45、TypeEvaluator使用实例:自定义颜色估值器

private class HsvEvaluator implements TypeEvaluator<Integer> {
    float[] startHsv = new float[3];
    float[] endHsv = new float[3];
    float[] outHsv = new float[3];

    @Override
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        // 把 ARGB 转换成 HSV
        Color.colorToHSV(startValue, startHsv);
        Color.colorToHSV(endValue, endHsv);

        // 计算当前动画完成度(fraction)所对应的颜色值
        if (endHsv[0] - startHsv[0] > 180) {
            endHsv[0] -= 360;
        } else if (endHsv[0] - startHsv[0] < -180) {
            endHsv[0] += 360;
        }
        outHsv[0] = startHsv[0] + (endHsv[0] - startHsv[0]) * fraction;
        if (outHsv[0] > 360) {
            outHsv[0] -= 360;
        } else if (outHsv[0] < 0) {
            outHsv[0] += 360;
        }
        outHsv[1] = startHsv[1] + (endHsv[1] - startHsv[1]) * fraction;
        outHsv[2] = startHsv[2] + (endHsv[2] - startHsv[2]) * fraction;

        // 计算当前动画完成度(fraction)所对应的透明度
        //右移动24位, ARGB 一共32位,每8位代表一个属性,依次代表透明度(alpha)、红色(red)、绿色(green)、蓝色(blue)。
        int alpha = startValue >> 24 + (int) ((endValue >> 24 - startValue >> 24) * fraction);

        // 把 HSV 转换回 ARGB 返回
        return Color.HSVToColor(alpha, outHsv);
    }
}

//使用一:
ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xffff0000, 0xff00ff00);
// 使用二:
ObjectAnimator animator = ObjectAnimator.ofObject(view, "color",
new HsvEvaluator(), 0xffff0000, 0xff00ff00);
// 使用自定义的 HslEvaluator
animator.setEvaluator(new HsvEvaluator());  
animator.start();  

46、ofObject()的使用

  1. 属性动画可以借助自定义TypeEvaluator通过ofObject()来对不限定类的属性做动画
//API21中已经实现,理解思路
private class PointFEvaluator implements TypeEvaluator<PointF> {  
   PointF newPoint = new PointF();

   @Override
   public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
       float x = startValue.x + (fraction * (endValue.x - startValue.x));
       float y = startValue.y + (fraction * (endValue.y - startValue.y));

       newPoint.set(x, y);

       return newPoint;
   }
}

ObjectAnimator animator = ObjectAnimator.ofObject(view, "position",  
        new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
animator.start();  

47、新API中新增了ofMultiInt()、ofMultiFloat()等方法

动画原理(8题)

View动画原理

1、View动画(Animation)的原理

  1. Animation内部通过ViewRootImplscheduleTraversals来监听下一个屏幕刷新信号
  2. 当接收到信号时,会从DecorView开始遍历View树并进行绘制
  3. 绘制过程中顺带将View绑定的动画执行
  4. 监听下一个屏幕刷新信号是通过Choreographer完成的,可以参考屏幕刷新机制

属性动画原理

2、属性动画为什么需要get/set方法?

  1. 属性动画通过传递给set的值不一样,并且越来越接近最终值,最终实现动画效果
  2. 如果动画时没有传递初始值,则需要通过get方法获取属性的初始值
  3. 如果初始值已经有了,则不需要get方法

3、ObjectAnimator的start()流程

  1. start()会先判断:若当前东、等待的动画和延迟的动画中有和当前动画相同的动画,就会取消相同的动画;最终调用父类ValueAnimatorstart()
  2. ValueAnimator中属性动画需要运行在Looper线程中;最终会调用AnimationHandler的start方法,此AnimationHandler并不是Handler,而是Runnable
  3. 该Runnable中涉及JNI层的交互,最终是进入到ValueAnimatior的doAnimationFrame方法
  4. doAnimationFrame中最后调用animationFrame()方法,其内部调用animateValue()方法
  5. animateValue()calculateValue()用于计算每帧动画所对应的属性值。
  6. 初始化时,若属性初始值没有提供,则调用get方法:PropertyValuesHolder中的setupValue,通过反射调用的get方法
  7. 当动画下一帧动画到来时,PropertyValuesHolder中的setAnimatedValue方法会将新的属性值设置给对象,通过反射调用其set方法

4、属性动画原理要点

  1. 属性动画需要运行在Looper线程中
  2. 初始化时,若没有提供属性初始值,PropertyValuesHoldersetupValue,通过反射调用的get方法
  3. 当动画下一帧动画到来时,PropertyValuesHoldersetAnimatedValue方法会通过反射调用其set方法,设置新的属性值

ValueAnimator原理

动画注册

5、ValueAnimator的start中的注册流程

//ValueAnimator.java
public void start() {
    start(false);
}

//ValueAnimator.java
private void start(boolean playBackwards) {
    // 1. 一些变量的初始化
    mStarted = true;
    mStartedDelay = false;
    mPaused = false;
    /**==============================*
     * 2. AnimationHandler
     *  1- 属性动画需要运行在Looper线程池中
     *  2- AnimationHandler本身是Runnable
     *==============================*/
    AnimationHandler animationHandler = getOrCreateAnimationHandler();
    animationHandler.mPendingAnimations.add(this);
    if (mStartDelay == 0) {
        //xxx
    }
    // 3. 开始
    animationHandler.start();
}

//ValueAnimator.java的内部类:AnimationHandler的方法
public void start() {
    scheduleAnimation();
}

//ValueAnimator.java的内部类:AnimationHandler的方法
private void scheduleAnimation() {
    if (!mAnimationScheduled) {
        /**==========================
         * 1. 在下一帧刷新信号到来时,执行Runnable mAnimate
         *===========================*/
        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
        mAnimationScheduled = true;
    }
}

//Choreographer.java
public void postCallback(int callbackType, Runnable action, Object token) {
    //在下一帧刷新信号到来时,调用Runnable
    postCallbackDelayed(callbackType, action, token, 0);
}
  1. start()方法最终会调用到AnimationHandlerscheduleAnimation()
  2. 通过ChoreographerpostCallback()去注册下一个刷新信号
  3. 下一帧刷新信号到达时,Choreographer会调用注册的Runnable
动画处理

6、接收到刷新信号后的动画处理过程

/**
 * //ValueAnimator.java的内部类:AnimationHandler中
 *  1. 被Choreographer调用
 */
final Runnable mAnimate = new Runnable() {
    @Override
    public void run() {
        mAnimationScheduled = false;
        doAnimationFrame(mChoreographer.getFrameTime());
    }
};

//ValueAnimator.java的内部类:AnimationHandler
void doAnimationFrame(long frameTime) {
    /**
     * 1、遍历待执行动画队列
     */
    for (int i = 0; i < mPendingAnimations.size(); ++i) {
        ValueAnimator anim = pendingCopy.get(i);
        // 2、无延迟动画直接开始,有延迟的动画添加到延迟队列
        if (anim.mStartDelay == 0) {
            /**
             * 1. 动画的初始化工作
             * 2. 添加到AnimationHandler的mAnimations队列中
             */
            anim.startAnimation(this);
        } else {
            // 加入到延迟队列中
            mDelayedAnims.add(anim);
        }
    }

    // 3、将 Delayed动画队列中的动画添加到 active的动画队列中
    for (int i = 0; i < mDelayedAnims.size(); ++i) {
        mReadyAnims.add(anim);
    }
    // 4、遍历active的动画队列,将其中的动画都执行startAnimation
    for (int i = 0; i < mReadyAnims.size(); ++i) {
        ValueAnimator anim = mReadyAnims.get(i);
        // 添加到AnimationHandler的mAnimations队列中
        anim.startAnimation(this);
    }

    /**
     *  5、临时列表进行存储
     */
    int numAnims = mAnimations.size();
    for (int i = 0; i < numAnims; ++i) {
        mTmpAnimations.add(mAnimations.get(i));
    }
    /**
     *  6、执行所有的active状态的动画
     *      1-doAnimationFrame 执行动画
     *      2-将执行完的动画加入到 mEndingAnims
     */

    for (int i = 0; i < numAnims; ++i) {
        ValueAnimator anim = mTmpAnimations.get(i);
        if (anim.doAnimationFrame(frameTime)) {
            mEndingAnims.add(anim);
        }
    }
    /**
     * 7、动画进行结束操作
     */
    for (int i = 0; i < mEndingAnims.size(); ++i) {
        mEndingAnims.get(i).endAnimation(this);
    }

    // 8、该帧画面的最后提交
    mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);

    // 9、如果还有需要执行的动画和延迟动画,则监听下一帧刷新信号
    if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
        scheduleAnimation();
    }
}

//ValueAnimator.java-处理一帧的动画
final boolean doAnimationFrame(long frameTime) {
    // 1、处理第一帧动画的工作
    if (mSeekFraction < 0) {
        mStartTime = frameTime;
    } else {
        long seekTime = (long) (mDuration * mSeekFraction);
        mStartTime = frameTime - seekTime;
        mSeekFraction = -1;
    }
    //2、修正第一帧动画的时间
    final long currentTime = Math.max(frameTime, mStartTime);
    //3、计算出当前时间所对应的属性数值
    return animationFrame(currentTime);
}

//ValueAnimator.java
boolean animationFrame(long currentTime) {
    boolean done = false;
    //1、计算出fraction
    float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
    //xxx
    //2、通过插值器计算出最终的fraction和数值,并回调onAnimationUpdate
    animateValue(fraction);
    //3、返回是否处理完成
    return done;
}

//ValueAnimator.java-将动画从active动画列表、延迟列表、待执行列表中移除
protected void endAnimation(AnimationHandler handler) {
    handler.mAnimations.remove(this);
    handler.mPendingAnimations.remove(this);
    handler.mDelayedAnims.remove(this);
    //xxx
}
  1. Choreographer会调用Runnable中的方法
  2. 会执行AnimationHandlerdoAnimationFrame()
  3. 根据当前时间计算出属性值
  4. 将执行完的动画移除动画队列
  5. 如果动画队列中有未执行完的动画,通过Choreographer去注册下一帧刷新信号
  6. 循环往复直至动画队列中所有动画都执行完毕。

7、第一帧动画的时间矫正

  1. 在第一帧动画开始前,会进行三大流程,假如耗时过多会导致前几帧动画的丢失。
  2. 如果动画还未开始就丢失几帧画面是不合理的,在doAnimationFrame处理动画时,会对第一帧动画的开始时间进行校正。
  3. 该工作只对第一帧有效,防止丢帧。但是如果动画中途出现丢帧是无法处理的。

8、ValueAnimator补充点

  1. ValueAnimator本身不涉及UI操作,需要在回调中进行UI变化

动画的要点总结(2)

1、动画使用的7个注意点

  1. OOM:图片数量较多或者图片较大时容易出现OOM,且尽量避免帧动画
  2. 内存泄露:属性动画中无限循环动画,需要在Acitivty退出后及时停止,否则会导致Activity无法释放。验证后发现View动画并不存在此问题。
  3. 兼容性问题: 3.0以下系统上有兼容问题,需要适配
  4. View动画的问题:View动画并不是真正改变View的状态,可能会动画之后View的setVisibility(GONE)失效,需要调用view.clearAnimation()清除View动画后才能解决
  5. 不要使用px:要使用dp,px会导致不同设备上有不同效果
  6. 动画元素的交互:3.0后,属性动画点击事件会跟随View而移动,View动画会停留在原位置
  7. 硬件加速: 建议开启硬件加速,会提高动画的流畅性

2、复杂属性动画大致三种方法

  1. 使用 PropertyValuesHolder 来对多个属性同时做动画;
  2. 使用 AnimatorSet 来同时管理调配多个动画;
  3. 使用 PropertyValuesHolder.ofKeyframe() 来把一个属性拆分成多段,执行更加精细的属性动画。

知识储备(1)

1、HSV是什么?

HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)。
1. 色调H
用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;
2. 饱和度S
饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
3. 明度V
明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。

参考资料

  1. 插值器网站
  2. 三次贝塞尔曲线-效果查看
  3. 弹性动画-3种实现方法
  4. 动画3PropertyAnimator ValueAnimator
  5. PropertyValuesHolders.ofKeyframe()详解
  6. Android 动画:手把手教你使用 补间动画

猜你喜欢

转载自blog.csdn.net/feather_wch/article/details/81503233
今日推荐