Android Animation和Animator

前言:写这一篇文章的目的是记录一个自己犯下的一个蠢萌的问题,顺便复习一下Animation和Animator。最近好像喜欢上了写博客,我发现写博客不仅可以增强自己的表达能力,还可以强化巩固自己学习过的知识点,毕竟你要向读者讲明白一件事情,首先要自己搞清楚才行。

一、一个很蠢的Bug

  今天遇到了一个SB的Bug, 不是Bug是SB,是我SB。我在写ViewsFlipper–最易用的的仿淘宝、京东消息轮播控件这篇文章的时候,想给增加一个设置动画滚动方向的功能,因为本来只能垂直滚动,那么增加一下水平滚动好像也不是什么难事,将Animator的Property根据传入的方向设置为“translationX”或者“translationY”,然后赋予不同的Values即可。然后build-Run,点击按钮改变滚动方向,what?我的View怎么不见了?!
  刚开始我怀疑是因为我的View被隐藏了,因为我有在代码中设置动态的去改变View的可见性,但是我通过Log和Debug发现,此时的View的Visibility是VISIBLE的!那就奇怪了?难道是我的Animator的Property或者Values没有设置正确?于是我又检查了这两个属性,发现好像也没问题。但是在Debug的时候,我注意到此时的View的位置好像有点不对,此时的View的x轴Left坐标是负的!并且x轴的Right坐标是0!怪不得我在屏幕上看不到View,但是它的Visibility却是VISIBLE的,尼玛的原来它在屏幕范围外面啊!
  那么谁将它移动到外面去了呢?显而易见是我自己啊!先给自己一巴掌。至于原因呢,当然是Animator的功劳了。不同于Android的Animation,在动画执行完成之后,View会恢复初始位置,因为使用Animation不会改变View的位置。但是使用Animator却会改变View的位置,并且永远改变!!!你要是取消或者结束了动画,View执行到哪里就会停在哪里,不会恢复到初始位置。因为View在水平滚动的时候,执行退出动画时将View从0向左移动View一个屏幕宽度的距离,从而达到退场的目的。但是我在改变View滚动方向的函数里面,并没有考虑到已经有个View被我无情的抛出到屏幕外面了(我以为它在屏幕里面),所以导致在执行垂直滚动的时候,在屏幕外面的View就自个自的在看不到的地方滚动。
  所以知识点要掌握牢固啊!不然就会像我这样,混淆了Animator和Animation,白白浪费了大把时间去做无用功。? 下面就简单讲一下Animation和Animator吧,后面有时间再系统的整理一下Android动画方面的知识。

二、Animation

  动画无外乎平移、旋转、缩放、透明度,对应的Android的Animation动画类分别是TranslateAnimation、RotateAnimation、ScaleAnimation、AlphaAnimation,他们都继承自Animation抽象类。先介绍一下Animation基类中几个重要的公共属性。

name作用
duration动画时长,单位是毫秒(ms)
repeatCount动画重复次数,默认0次,当设置次数小于0时,代表重复无限次
repeatMode重复类型,有Animation.RESTART和Animation.REVERSE两种,分别代表动画结束时是从头开始反向动画,需要设置repeatCount为Animation.INFINITE才会生效,默认是RESTART
interpolator插值器,控制动画执行速度,比如匀速、加速、减速执行等。
fillBefore动画执行完成之后是否回到初始位置,默认true
fillAfter动画执行完成之后是否留在原地,默认false

以上属性都可以在代码中设置,也可以在xml文件中以标签的形式指定。Animation的XML格式的动画,都存放在res/anim/目录下,分别有不同的标签。Animation并不能改变View的真实位置!!!它只能改变View的显示位置,如果你在平移或旋转之后的地方点击,是不会响应点击事件的,你要点击View原来的点击事件还是会响应点击事件的!!!

2.1 TranslateAnimation平移动画

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:fromXDelta="0%"
    android:fromYDelta="0%"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:toXDelta="100%"
    android:toYDelta="100%" />

  TranslateAnimation类对应的xml标签是translate,除了Animation通用属性,Translation还有自己的属性。

name作用
android:fromXDelta动画起点的X轴坐标值,可以是数值、百分值、百分值+p,分别代表在当前View的左上角,X轴加上数值(如20代表加上20px)的距离,百分值 就是加上自身宽度的百分值(如20%代表加上View width * 20%,百分值+p 就是加上父布局的宽度的百分值(如20%p代表加上Parent View width * 20%)作为起点X轴坐标。
android:fromYDelta动画起点的Y轴坐标值,属性同上
android:toXDelta动画终点的X轴坐标值,属性同上
android:toYDelta动画终点的Y轴坐标值,属性同上

TranslateAnimation的代码生成是通过其构造函数生成的,

  • public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
  • public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
    int fromYType, float fromYValue, int toYType, float toYValue)

上面是常用到的两个构造函数,其中第一个构造函数的值,只能传递具体的数值,对应xml中的数值,而第二个构造函数就和xml中一样了,可以传递数值,百分值,百分值+p,具体是哪一种,取决于参数前面的一个Type参数。该参数有三种类型,Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, orAnimation.RELATIVE_TO_PARENT,分别代表绝对值、相对自身和相对父布局,和xml一一对应。下面看一下实战例子,最近主要在使用kotlin,所以展示代码也用kotlin来写了,代码和java差不多,会java应该不难看懂。

    val animation = if (fromXml) {
        AnimationUtils.loadAnimation(this, R.anim.translate_anim)
    } else {
        val animationCode = TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1f,
            Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1f
        )
        animationCode.duration = 3000L
        animationCode.fillAfter = true
        animationCode.interpolator = AccelerateInterpolator()
        animationCode
    }
    iv_animation_like.startAnimation(animation)

在XML中设置的TranslateAnimation和在代码中生成的TranslateAnimation都使用了相对自身的属性,不同的是XML中设置重复无数次,然后往返运动(因为设置了repeatMode是reverse),代码中没有设置,默认不重复,但是动画结束后会停在原地(设置的fillAfter为true)。

2.2 RotateAnimation旋转动画

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:fillBefore="true"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:toDegrees="180" />

RotateAnimation对应的xml标签是rotate,它的属性有

name作用
fromDegrees开始旋转的角度
toDegrees旋转结束的角度
pivotX旋转的中心点X坐标,可以是数值,百分值,百分值+p
pivotY旋转的中心点Y坐标,可以是数值,百分值,百分值+p

构造函数:

  • public RotateAnimation(float fromDegrees, float toDegrees)
  • RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY)
  • RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
    int pivotYType, float pivotYValue)

如果没有设置旋转中心点,默认是0,其他没啥好讲的,和TranslateAnimation一样,上Code,看动画。

	val animation = if (fromXml) {
        AnimationUtils.loadAnimation(this, R.anim.rotate_anim)
    } else {
        val animationCode = RotateAnimation(0f, 180f)
        animationCode.duration = 3000L
        animationCode.fillAfter = true
        animationCode.interpolator = AccelerateInterpolator()
        animationCode
    }
    iv_animation_like.startAnimation(animation)

2.3 ScaleAnimation缩放动画

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:fillAfter="true"
    android:fromXScale="100%"
    android:fromYScale="100%"
    android:pivotX="50"
    android:pivotY="50"
    android:toXScale="50%"
    android:toYScale="50%" />

ScaleAnimation对应的xml标签是scale ,它的属性有

name作用
fromXScaleX轴开始缩放时的大小
fromYScaleY轴开始缩放时的大小
toXScaleX轴结束缩放时的大小
toYScaleY轴结束缩放时的大小
pivotX缩放的中心点X坐标,可以是数值,百分值,百分值+p
pivotY缩放的中心点Y坐标,可以是数值,百分值,百分值+p

构造函数:

  • ScaleAnimation(float fromX, float toX, float fromY, float toY)
  • ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY)
  • ScaleAnimation(float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)

构造函数和XML设置一一对应,不再多言,上示例代码,看动画

    val animation = if (fromXml) {
        AnimationUtils.loadAnimation(this, R.anim.scale_anim)
    } else {
        val animationCode = ScaleAnimation(1f, 2f, 1f, 2f)
        animationCode.duration = 3000L
        animationCode.fillAfter = true
        animationCode.interpolator = AccelerateInterpolator()
        animationCode
    }
    iv_animation_like.startAnimation(animation)

2.4 AlphaAnimation透明度动画

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:fromAlpha="0.0"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:toAlpha="1.0" />

AlphaAnimation对应的xml标签是alpha ,它的属性有

name作用
fromAlpha动画开始时的透明度
toAlpha动画结束时的透明度(0f—1.0f)

构造函数:

  • AlphaAnimation(float fromAlpha, float toAlpha)
	val animation = if (fromXml) {
        AnimationUtils.loadAnimation(this, R.anim.alpha_anim)
    } else {
        val animationCode = AlphaAnimation(1f, 0.1f)
        animationCode.duration = 3000L
        animationCode.fillAfter = true
        animationCode.interpolator = AccelerateInterpolator()
        animationCode
    }
    iv_animation_like.startAnimation(animation)

三、Animator

  终于讲到导致我出bug的罪魁祸首了,不过今天就先到这里吧,明天起来再整理一下资料再写下去。睡了。。。
  早起查阅了一上午资料,发现Animator的东西有点多,一篇文章大概是讲不完了,回头更新到新的文章,写好之后在这里放个链接吧。

四、源码链接

依旧是”全球最大同性交友网站“github,路径me.fresh.lee.kotlintest.activity.AnimationActivity

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值