Connor学Android - Android动画

在这里插入图片描述

Learn && Live

虚度年华浮萍于世,勤学善思至死不渝

前言

Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/2kSSX,话不多说我们马上开始!

Android动画可以分为三种:View动画、帧动画和属性动画

  • View动画:通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,且支持自定义
  • 帧动画:通过顺序播放一系列图像从而产生动画效果。可以简单理解为图片切换动画,如果图片过多过大会导致OOM
  • 属性动画:通过动态改变对象的属性从而达到动画效果

7.1 View动画

View动画对应四种动画效果:平移动画、缩放动画、旋转动画和透明度动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    >
    <scale
        android:duration="3000"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.0"
        android:toYScale="1.0"/>
    <alpha
        android:duration="3000"
        android:fromAlpha="1.0"
        android:toAlpha="0.5" />
    <rotate
        android:fromDegrees="0"
        android:toDegrees="720"
        android:pivotX = "50%"
        android:pivotY="50%"
        android:duration = "3000"
        />
    <translate
        android:fromXDelta="0"
        android:toXDelta="100"
        android:fromYDelta="0"
        android:toYDelta="100" />
</set>

<set> 标签表示动画集合,对应 AnimationSet 类,可以包含若干个动画,且内部可以嵌套其他动画集合

  • android:interpolator:表示动画集合的插值器,可以影响动画的速度
  • android:shareInterpolator:表示集合中的动画是否和集合共享插值器。如果集合不指定插值器,则子动画需要单独指定插值器或使用默认值
1.平移动画

<translate> 表示平移动画,对应 TranslateAnimation 类,可以使一个View在水平和竖直方向完成平移的动画效果

  • android;fromXDelta:表示x的起始值
  • android:toXDelta:表示x的结束值
  • android:fromYDelta:表示y的起始值
  • android:toYDelta:表示y的结束值
2.缩放动画

<scale> 表示缩放动画,对应 ScaleAnimation 类,可以使View具有放大或缩小的动画效果

  • android:fromXScale:水平方向缩放的起始值
  • android:toXScale:水平方向缩放的结束值
  • android:fromYScale:竖直方向缩放的起始值
  • android:toYScale:竖直方向缩放的结束值
  • android:pivotX:缩放轴点的x坐标
  • android:pivotY:缩放轴点的y坐标

默认情况下轴点是View的中心点,此时在水平方向上缩放会导致View向左右两个方向同时缩放,但是如果把轴点设为View的右边界,那么View就只会向左边进行缩放,反之则向右边进行缩放

3.旋转动画

<rotate> 表示旋转动画,对应 RotateAnimation 类,可以使View具有旋转的动画效果

  • android:fromDegrees:旋转开始的角度
  • android:toDegrees:旋转结束的角度
  • android:pivotX:旋转轴点的x坐标
  • android:pivotY:旋转轴点的y坐标

默认情况下轴点是View的中心点

4.透明度动画

<alpha> 表示透明度动画,对应 AlphaAnimation 类,可以改变View的透明度

  • android:fromAlpha:透明度的起始值
  • android:toAlpha:透明度的结束值
5.其他属性
  • android:duration:动画持续时间
  • android:fillAfter:动画结束后是否停留在结束位置
6.使用动画

定义动画,可以通过XML定义也可以在代码中动态定义

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
	 android:interpolator="@android:anim/accelerate_decelerate_interpolator">
    <rotate
        android:fromDegrees="0"
        android:toDegrees="90"
        android:duration = "400" />
    <translate
        androidLduration="100"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:interpolator="@android:anim/linear_interpolator"
        android:toXDelta="100"
        android:toYDelta="100" />
</set>

Button mButton = findViewById(R.id.button1);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animationtest);
mButton.startAnimation(animation);
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(300);
mButton.startAnimation(alphaAnimation);
7.自定义View动画

(1)继承抽象类Animation

(2)重写initialize和applyTransformation方法,在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩阵变换即可

8.View动画的特殊使用场景

LayoutAnimation

作用于ViewGroup,为ViewGroup指定一个动画,在其子元素显示时会具有该动画效果。使用步骤如下:

(1)定义为子元素指定的具体的动画

// res/anim/anim_item.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:duration="300"
	 android:interpolator="@android:anim/accelerate_decelerate_interpolator"
     android:shareInterpolator="true">
    <alpha
           android:fromAlpha="0.0"
           android:toAlpha="1.0" />
    <translate
           android:fromXDelta="500"
           android:toXDelta="0" />
</set>

(2)定义LayoutAnimation

// res/anim/anim_layout.xml
<layoutAnimation
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:delay="0.5"
   android:animationOrder="normal"
   android:animation="@anim/anim_item" />
  • android:delay:子元素开始动画的时间延迟,比如子元素入场动画的时间周期为300ms,则0.5表示每个子元素都需要延迟150ms再播放动画
  • android:animationOrder:子元素动画顺序。normal表示顺序显示,排在前面的子元素先播放动画;reverse表示逆向显示;random则表示随机播放动画
  • android:animation:指定具体的动画,这里指定(1)中定义的动画

(3)为ViewGroup指定android:layoutAnimation属性

<ListView
     ......
     android:layoutAnimation="@anim/anim_layout" />

除了在XML中指定LayoutAnimation外,还可以通过LayoutAnimationController实现

ListView listView = findViewById(R.id.list);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);

Activity的切换效果

通过overridePendingTransition(int enterAnim, int exitAnim)方法实现

  • enterAnim:打开Activity时所需的动画资源id
  • exitAnim:Activity被暂停时所需的动画资源id
// 启动Activity
Intent intent = new Intent(this, TestActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);

// 退出Activity
@Override
public void finish() {
    super.finish();
    overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
}

Fragment的切换效果

可以通过FragmentTransaction中的setCustomAnimations()方法来添加切换动画,注意这里的动画需要是View动画,因为Fragment和属性动画都是API 11新引入的

7.2 帧动画

(1)通过XML定义一个AnimationDrawable

// res/drawable/frame_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
	android:oneshot="false">
    <item android:drawable="@drawable/image1" android:duration="500" />
    <item android:drawable="@drawable/image2" android:duration="500" />
    <item android:drawable="@drawable/image3" android:duration="500" />
</animation-list>

(2)使用AnimationDrawable

Button mButton = findViewById(R.id.button1);
mButton.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable) mButton.getBackground();
drawable.start();

帧动画比较容易引起OOM,所以在使用帧动画时应尽量避免使用过多尺寸较大的图片

7.3 属性动画

1.使用属性动画

(1)属性动画可以对任意对象的属性进行动画而不仅仅是 View

(2)可以实现的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变,默认间隔为 300ms,默认帧率为 10ms/帧

(3)可以通过代码或 XML 文件两种方式使用属性动画

代码实现

属性动画涉及到的动画类有 ValueAnimator、ObjectAnimator、AnimatorSet

(1)ObjectAnimator:继承自 ValueAnimator,通过调用 ofXxx() 方法指定对象、属性、数值,Xxx为数值类型,最后调用 start() 方法

ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start()

(2)ValueAnimator:本身不作用于任何对象,通常通过上面 ObjectAnimator 的执行返回一个 ValueAnimator,再修改属性

ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", ..., ...);
colorAnim.setDuration(3000);
colorAnim.start();

(3)AnimatorSet:动画集合,可以调用 playTogether() 方法定义一组动画

AnimatorSet set = new AnimatorSet();
set.playTogether(
	ObjectAnimator.ofFloat(myView, "rotationX", 0, 360)
	...
);
set.setDuration(5000).start();

XML 实现

主要涉及三个标签:<objectanimator>、<animator>、<set>

(1)<objectanimator>:对应于 ObjectAnimator,主要包含以下属性

  • android:propertyName:表示属性动画的作用对象的属性名称
  • android:duration:表示动画的时长
  • android:valueFrom:表示属性的起始值
  • android:valueTo:表示属性的结束值
  • android:startOffset:表示动画的延迟时间,即动画开始后,需要延迟多少毫秒才开始真正播放此动画
  • android:repeatCount:表示动画的重复次数,默认值为0,-1表示无限循环
  • android:repeatMode:表示动画的重复模式
    • repeat:连续重复,动画每次都是重新开始播放
    • reverse:逆向重复,第一次播放完后第二次倒着播放,如此反复
  • android:valueType:表示 android:propertyName 指定的属性的类型
    • 有 intType 和 floatType 两个可选项,分别表示属性的类型为整型和浮点型
    • 如果 android:propertyName 指定的属性表示的是颜色,则不需要指定该属性,系统会自动对颜色类型的属性做处理
2.插值器与估值器

插值器

(1)TimeInterpolator,时间插值器,作用是根据时间改变的百分比来计算当前属性改变的百分比

(2)系统预置的插值器有

  • LinearInterpolator:线性插值器,匀速动画
  • AccelerateDecelerateInterpolator:加速减速插值器,动画先加速后减速,即两头慢中间快
  • DecelerateInterpolator:减速插值器,动画越来越慢

估值器

(1)TypeEvaluator,类型估值算法,作用是根据当前属性改变的百分比来计算改变后的属性值

(2)系统预置的估值器有

  • IntEvaluator:针对整型属性
  • FloatEvaluator:针对浮点型属性
  • ArgbEvaluator:针对 Color 属性

自定义

自定义插值器需要实现 Interpolator 或者 TimeInterpolator

自定义估值算法需要实现 TypeEvaluator

如果对其他类型(除 int float Color),必须要自定义类型估值算法

3.监听器

属性动画提供了监听器用于监听动画的播放过程,主要有两个接口:AnimatorUpdateListener 和 AnimatorListener

(1)AnimatorListener

  • 可以监听动画的开始、结束、取消以及重复播放
  • 对应实现 onAnimationStart \ End \ Cancel \ Repeat 方法

(2)AnimatorUpdateListener

  • 实现 onAnimationUpdate() 方法
  • 用于监听整个动画过程,动画每播放一帧,onAnimationUpdate() 就会被调用一次
4.对任意属性做动画

(1)在实际开发使用属性动画时,想要动画生效,需要对应的对象满足两个条件

  • 对象必须提供对应属性的 set 方法,如果使用动画的时候没有给定初始值,则还需要提供 get 方法,方便取该属性的初始值

  • 对象提供的 set 方法对该属性的改变必须能够通过某种方法反映出来,比如能改变 UI

(2)两个条件缺一不可,如果条件一不满足则程序直接 Crash,如果条件二不满足则动画无效果但不会 Crash

(3)当两条件不满足时,可以有三种解决办法

  • 给对象加上 getter 和 setter
  • 用一个类来包装原始对象,间接为其提供 getter 和 setter
private void performAnimate() {
    ViewWrapper wrapper = new ViewWrapper(mButton);
    ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
}

private static class ViewWrapper {
    private View mTarget;
    
    public ViewWrapper(View target) {
        mTarget = target;
    }
    public int getWidth() {
        return mTarget.getLayoutParams().width;
    }
    public void setWidth(int width) {
        mTarget.getLayoutParams().width = width;
        mTarget.requestLayout();
    }
}
  • 借助 ValueAnimator,监听动画过程,在动画过程中修改对象的属性值,从而实现属性的改变
5.工作原理

ObjectAnimator.start

(1)start() 内首先会判断如果当前动画、等待动画(Pending)和延迟动画(Delay)中有和当前动画相同的动画,则将相同的动画取消掉

(2)然后调用 super.start(),之前说过 ObjectAnimator 继承自 ValueAnimator,因此调用 ValueAnimator 内的 start 方法

(3)ValueAnimator 内的 start 方法内会经历如下过程

  • 属性动画运行在有 Looper 的线程中
  • 最终会调用 AnimationHandler 的 start 方法
  • 这个 AnimationHandler 并非 Handler,而是一个 Runnable,在它的 run() 内会调用 ValueAnimator 的 doAnimationFrame 方法

(4)doAnimationFrame 方法又会调用 animationFrame() ,进而调用 animateValue() 方法

(5)最后在 animateValue() 中调用 calculateValue() 方法计算每帧动画所对应的属性的值,从而完成改变

get

(1)之前说过,在初始化的时候,如果属性的初始值没有提供,则 get 方法将会被调用

(2)这个过程发生在 PropertyValuesHolder 的 setuoValue方法中,在这个方法中可以看出 get 方法是通过反射来调用的

set

(1)当动画的下一帧到来时,PropertyValuesHolder 中的 setAnimatedValue 方法会将新的属性值设置给对象

(2)其中调用 setter 方法也是通过反射来调用的

7.4 使用动画的注意事项

OOM 问题

主要出现在帧动画中,当图片数量较多且图片较大时极容易出现 OOM,实际开发应尽量避免使用帧动画

内存泄露

主要针对属性动画中的循环动画,这类动画需要在Activity 退出时即时停止,否则会导致 Activity 无法释放从而造成内存泄露

View 动画的问题

View 动画有时会出现动画完成后 View 无法隐藏的现象,即 setVisibility(GONE) 失效,此时可调用 view.clearAnimation() 消除动画

不要使用 px

尽量使用 dp 而非 px,px 可能会导致适配问题

硬件加速

使用动画的过程中,建议开启硬件加速,这样会提高动画的流畅性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ConnorYan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值