Android动画的理解

基础知识

在我们开始讲Android动画这个知识点之前,我们了解下相应的基础知识点。

Shape篇

一般用Shape定义的XML文件是存放在Drawable目录下,广泛应用于在Button、TextView、EditText等各种基本控件中,来实现不同背景、颜色、边框。

使用Shape可以自定义形状,可以定义下面四种类型的形状,通过android:shape属性指定:

  • rectangle:矩形,默认的形状
  • oval:椭圆形,可以画正圆
  • line:线形,可以画实线和虚线
  • ring:环形,可以话进度条之类

rectange

  • solid: 设置形状填充的颜色,只有android:color一个属性
    • android:color 填充的颜色
  • padding: 设置内容与形状边界的内间距,可分别设置左右上下的距离
    • android:left 左内间距
    • android:right 右内间距
    • android:top 上内间距
    • android:bottom 下内间距
  • gradient: 设置形状的渐变颜色,可以是线性渐变、辐射渐变、扫描性渐变
    • android:type 渐变的类型linear 线性渐变,默认的渐变类型radial 放射渐变,设置该项时,android:gradientRadius也必须设置sweep 扫描性渐变
    • android:startColor 渐变开始的颜色
    • android:endColor 渐变结束的颜色
    • android:centerColor 渐变中间的颜色
    • android:angle 渐变的角度,线性渐变时才有效,必须是45的倍数,0表示从左到右,90表示从下到上
    • android:centerX 渐变中心的相对X坐标,放射渐变时才有效,在0.0到1.0之间,默认为0.5,表示在正中间
    • android:centerY 渐变中心的相对X坐标,放射渐变时才有效,在0.0到1.0之间,默认为0.5,表示在正中间
    • android:gradientRadius 渐变的半径,只有渐变类型为radial时才使用
    • android:useLevel 如果为true,则可在LevelListDrawable中使用
  • corners: 设置圆角,只适用于rectangle类型,可分别设置四个角不同半径的圆角,当设置的圆角半径很大时,比如200dp,就可变成弧形边了
    • android:radius 圆角半径,会被下面每个特定的圆角属性重写
    • android:topLeftRadius 左上角的半径
    • android:topRightRadius 右上角的半径
    • android:bottomLeftRadius 左下角的半径
    • android:bottomRightRadius 右下角的半径
  • stroke: 设置描边,可描成实线或虚线。
    • android:color 描边的颜色
    • android:width 描边的宽度
    • android:dashWidth 设置虚线时的横线长度
    • android:dashGap 设置虚线时的横线之间的距离

oval

它也有solid、padding、stroke、gradient、size几个特性,size是用来设置形状大小的,如下:

size: 设置形状默认的大小,可设置宽度和高度

android:width 宽度

android:height 高度

line

画线时,有几点特性必须要知道的:

  1. 只能画水平线,画不了竖线;
  2. 线的高度是通过stroke的android:width属性设置的;
  3. size的android:height属性定义的是整个形状区域的高度;
  4. size的height必须大于stroke的width,否则,线无法显示;
  5. 线在整个形状区域中是居中显示的;
  6. 线左右两边会留有空白间距,线越粗,空白越大;
  7. 引用虚线的view需要添加属性android:layerType,值设为”software”,否则显示不了虚线。

ring

首先,shape根元素有些属性只适用于ring类型,先过目下这些属性吧:

  • android:innerRadius 内环的半径
  • android:innerRadiusRatio 浮点型,以环的宽度比率来表示内环的半径,默认为3,表示内环半径为环的宽度除以3,该值会被android:innerRadius覆盖
  • android:thickness 环的厚度
  • android:thicknessRatio 浮点型,以环的宽度比率来表示环的厚度,默认为9,表示环的厚度为环的宽度除以9,该值会被android:thickness覆盖
  • android:useLevel 一般为false,否则可能环形无法显示,只有作为LevelListDrawable使用时才设为true
Selector篇

shape只能定义单一的形状,而实际应用中,很多地方比如按钮、Tab、ListItem等都是不同状态有不同的展示形状。举个例子,一个按钮的背景,默认时是一个形状,按下时是一个形状,不可操作时又是另一个形状。有时候,不同状态下改变的不只是背景、图片等,文字颜色也会相应改变。而要处理这些不同状态下展示什么的问题,就要用selector来实现了。

那么,看看都有哪些状态可以设置呢:

  • android:state_enabled: 设置触摸或点击事件是否可用状态,一般只在false时设置该属性,表示不可用状态
  • android:state_pressed: 设置是否按压状态,一般在true时设置该属性,表示已按压状态,默认为false
  • android:state_selected: 设置是否选中状态,true表示已选中,false表示未选中
  • android:state_checked: 设置是否勾选状态,主要用于CheckBox和RadioButton,true表示已被勾选,false表示未被勾选
  • android:state_checkable: 设置勾选是否可用状态,类似state_enabled,只是state_enabled会影响触摸或点击事件,而state_checkable影响勾选事件
  • android:state_focused: 设置是否获得焦点状态,true表示获得焦点,默认为false,表示未获得焦点
  • android:state_window_focused: 设置当前窗口是否获得焦点状态,true表示获得焦点,false表示未获得焦点,例如拉下通知栏或弹出对话框时,当前界面就会失去焦点;另外,ListView的ListItem获得焦点时也会触发true状态,可以理解为当前窗口就是ListItem本身
  • android:state_activated: 设置是否被激活状态,true表示被激活,false表示未激活,API Level 11及以上才支持,可通过代码调用控件的setActivated(boolean)方法设置是否激活该控件
  • android:state_hovered: 设置是否鼠标在上面滑动的状态,true表示鼠标在上面滑动,默认为false,API Level 14及以上才支持
Layer-List篇

使用layer-list可以将多个drawable按照顺序层叠在一起显示。

带阴影的圆角矩形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 灰色阴影 -->
<item
android:left="2dp"
android:top="4dp">
<shape>
<solid android:color="@android:color/darker_gray" />
<corners android:radius="10dp" />
</shape>
</item>
<!-- 白色前景 -->
<item
android:bottom="4dp"
android:right="2dp">
<shape>
<solid android:color="#FFFFFF" />
<corners android:radius="10dp" />
</shape>
</item>
</layer-list>

从上面的示例代码可以看到,layer-list可以作为根节点,也可以作为selector中item的子节点。layer-list可以添加多个item子节点,每个item子节点对应一个drawable资源,按照item从上到下的顺序叠加在一起,再通过设置每个item的偏移量就可以看到阴影等效果了。layer-list的item可以通过下面四个属性设置偏移量:

  • android:top 顶部的偏移量
  • android:bottom 底部的偏移量
  • android:left 左边的偏移量
  • android:right 右边的偏移量

这四个偏移量和控件的margin设置差不多,都是外间距的效果。如何不设置偏移量,前面的图层就完全挡住了后面的图层,从而也看不到后面的图层效果了。

另外,关于item的用法,也做下总结:

  1. 根节点不同时,可设置的属性是会不同的,比如selector下,可以设置一些状态属性,而在layer-list下,可以设置偏移量;
  2. 就算父节点同样是selector,放在drawable目录和放在color目录下可用的属性也会不同,比如drawable目录下可用的属性为android:drawable,在color目录下可用的属性为android:color;
  3. item的子节点可以为任何类型的drawable类标签,除了上面例子中的shape、color、layer-list,也可以是selector,还有其他没讲过的bitmap、clip、scale、inset、transition、rotate、animated-rotate、lever-list等等。
Drawable资源篇

nine-patch标签

使用nine-patch标签可以对点九图片做一些设置处理,不过可设置的属性并不多:

  • android:src 必填项,必须指定点九类型的图片
  • android:dither 设置是否抖动,图片与屏幕的像素配置不同时会用到,比如图片是ARGB 8888的,而屏幕是RGB565
  • android:tint 给图片着色,比如图片本来是黑色的,着色后可以变成白色
  • android:tintMode 着色模式,API Level 21(Android 5.0)才添加的属性
  • android:alpha 设置图片的透明度,取值范围为0.0~1.0之间,0.0为全透明,1.0为全不透明,API Level最低要求是11
  • android:autoMirrored 设置图片是否需要镜像反转,当布局方向是RTL,即从右到左布局时才有用,API Level 19(Android 4.4)才添加的属性

scale标签

使用scale标签可以对drawable进行缩放操作,和clip一样是通过设置level来控制缩放的比例。scale标签可以设置的属性如下:

  • android:drawable 指定drawable资源,如果不设置该属性,也可以定义drawable类型的子标签
  • android:scaleHeight 设置可缩放的高度,用百分比表示,格式为XX%,0%表示不做任何缩放,50%表示只能缩放一半
  • android:scaleWidth 设置可缩放的宽度,用百分比表示,格式为XX%,0%表示不做任何缩放,50%表示只能缩放一半
  • android:scaleGravity 设置drawable缩放后的位置,取值和bitmap标签的一样,就不一一列举说明了,不过默认值是left
  • android:useIntrinsicSizeAsMinimum 设置drawable原有尺寸作为最小尺寸,设为true时,缩放基本无效,API Level最低要求为11

rotate标签

使用rotate标签可以对一个drawable进行旋转操作,在shape篇讲环形时最后举了个进度条时就用到了rotate标签。另外,比如你有一张箭头向上的图片,但你还需要一个箭头向下的图片,这时就可以使用rotate将向上的箭头旋转变成一张箭头向下的drawable。

先看看rotate标签的一些属性吧:

  • android:drawable 指定drawable资源,如果不设置该属性,也可以定义drawable类型的子标签
  • android:fromDegrees 起始的角度度数
  • android:toDegrees 结束的角度度数,正数表示顺时针,负数表示逆时针
  • android:pivotX 旋转中心的X坐标,浮点数或是百分比。浮点数表示相对于drawable的左边缘距离

Drawable Animation

引用官方文档的话

Drawable animation lets you load a series of Drawable resources one after another to create an animation. This is a traditional animation in the sense that it is created with a sequence of different images, played in order, like a roll of film.

其实说明动画的Drawable的资源按照一定顺序执行,最终出来的效果类似电影中一帧一帧的,它允许你实现像播放幻灯片一样的效果。

是它的根元素,做为根节点,下面就是一个例子

1
2
3
4
5
6
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
<item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
<item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>

这里定义了三个drawable资源文件,每次执行时间为200ms,顺序是从上到下,这种动画最常见应用场景是在想表达电池充电的过程,从0%,25%,50%,100%表达连续充电的过程,就可以做四个不同的资源图片,然后按照一定的顺序,进而执行相应的运行时间。

有了动画文件之后,具体怎么用呢,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
AnimationDrawable rocketAnimation;

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}

public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
rocketAnimation.start();
return true;
}
return super.onTouchEvent(event);
}

特别注意,AnimationDrawable的start()方法不能在Activity的onCreate方法中调运,因为AnimationDrawable还未完全附着到window上,所以最好的调运时机是onWindowFocusChanged()方法中。

其中Drawable Animation还有一个animated-rotate属性来定义旋转动画,这个用法也是类似的,不再过多讲述。

View Animation

引用官方文档

You can use the view animation system to perform tweened animation on Views. Tween animation calculates the animation with information such as the start point, end point, size, rotation, and other common aspects of an animation.

视图动画,也叫Tween(补间)动画可以在一个视图容器内执行一系列简单变换(位置、大小、旋转、透明度)。譬如,如果你有一个TextView对象,您可以移动、旋转、缩放、透明度设置其文本,当然,如果它有一个背景图像,背景图像会随着文本变化。

补间动画通过XML或Android代码定义,建议使用XML文件定义,因为它更具可读性、可重用性。

Animation控制的是整个View,实现的原理是每次绘制视图时,View所在的ViewGroup的drawChild函数获取该View的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧。如果动画没完成,就继续调用invalidate()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。

视图动画使用简单,主要有以下四种类型动画:

  • Alpha Animation
  • Rotate Animation
  • Translate Animation
  • Scale Animation

并提供了AnimationSet动画集合,混合使用多种动画方式,在Android 3.0之前,View Animation一家独大,但是随着Property Animation的推出,风光就大不如前,相比属性动画,视图动画一个最大的缺陷是不具备交互性,当某个元素发生视图动画后,其响应的事件的位置还依然在动画前的地方,所以视图动画只能做普通的动画效果,避免交互的发生,但它的优点是,效率高而且使用方便。

#####Alpha Animation

可以实现透明度渐变的动画效果,也就是淡入淡出的效果,可通过设置下面三个属性来设置淡入或淡出效果:

  • android:duration 动画从开始到结束持续的时长,单位为毫秒
  • android:fromAlpha 动画开始时的透明度,0.0为全透明,1.0为不透明,默认为1.0
  • android:toAlpha 动画结束时的透明度,0.0为全透明,1.0为不透明,默认为1.0

当设置开始时透明度为0.0,结束时为1.0,就能实现淡入效果;相反,当设置开始时透明度为1.0,结束时为0.0,那就能实现淡出效果。示例代码如下:

1
2
3
4
5
6
<!-- res/anim/fade_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="0.0"
android:toAlpha="1.0" />

将这动画效果添加到View上也只需要一行代码:

1
view.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade_in));

将这动画效果添加到View上也只需要一行代码:

#####Rotate Animation

可以实现旋转的动画效果,主要的属性如下:

  • android:duration 动画从开始到结束持续的时长,单位为毫秒
  • android:fromDegrees 旋转开始的角度
  • android:toDegrees 旋转结束的角度
  • android:pivotX 旋转中心点的X坐标,纯数字表示相对于View本身左边缘的像素偏移量;带”%”后缀时表示相对于View本身左边缘的百分比偏移量;带”%p”后缀时表示相对于父View左边缘的百分比偏移量
  • android:pivotY 旋转中心点的Y坐标,纯数字表示相对于View本身顶部边缘的像素偏移量;带”%”后缀时表示相对于View本身顶部边缘的百分比偏移量;带”%p”后缀时表示相对于父View顶部边缘的百分比偏移量

以下示例代码旋转角度从0到360,即旋转了一圈,旋转的中心点都设为了50%,即是View本身中点的位置。

1
2
3
4
5
6
7
8
<!-- res/anim/rotate_one.xml -->
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%" />

标签对应的类为RotateAnimation,父类也是Animation,添加到View上的代码如下:

1
2
RotateAnimation rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(this, R.anim.rotate_one);
view.startAnimation(rotateAnimation);

#####Translate Animation

可以实现位置移动的动画效果,可以是垂直方向的移动,也可以是水平方向的移动。坐标的值可以有三种格式:从-100到100,以”%”结束,表示相对于View本身的百分比位置;如果以”%p”结束,表示相对于View的父View的百分比位置;如果没有任何后缀,表示相对于View本身具体的像素值。主要的属性如下:

  • android:duration 动画从开始到结束持续的时长,单位为毫秒
  • android:fromXDelta 起始位置的X坐标的偏移量
  • android:toXDelta 结束位置的X坐标的偏移量
  • android:fromYDelta 起始位置的Y坐标的偏移量
  • android:toYDelta 结束位置的Y坐标的偏移量

看示例吧,以下代码实现的是从左到右的移动效果,起始位置为相对于控件本身-100%的位置,即在控件左边,与控件本身宽度一致的位置;结束位置为相对于父控件100%的位置,即会移出父控件右边缘的位置。

1
2
3
4
5
6
7
8
<!-- res/anim/move_left_to_right.xml -->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXDelta="-100%"
android:fromYDelta="0"
android:toXDelta="100%p"
android:toYDelta="0" />

标签对应的类为TranslateAnimation,父类也是Animation,添加到View上的代码如下:

1
2
TranslateAnimation moveAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.move_left_to_right);
view.startAnimation(moveAnimation);

#####Scale Animation

可以实现缩放的动画效果,主要的属性如下:

  • android:duration 动画从开始到结束持续的时长,单位为毫秒
  • android:fromXScale 动画开始时X坐标上的缩放尺寸
  • android:toXScale 动画结束时X坐标上的缩放尺寸
  • android:fromYScale 动画开始时Y坐标上的缩放尺寸
  • android:toYScale 动画结束时Y坐标上的缩放尺寸
    PS以上四个属性,0.0表示缩放到没有,1.0表示正常无缩放,小于1.0表示收缩,大于1.0表示放大
  • android:pivotX 缩放时的固定不变的X坐标,一般用百分比表示,0%表示左边缘,100%表示右边缘
  • android:pivotY 缩放时的固定不变的Y坐标,一般用百分比表示,0%表示顶部边缘,100%表示底部边缘

具体示例:

1
2
3
4
5
6
7
8
9
10
<!-- res/anim/zoom_out.xml -->
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="0%"
android:pivotY="100%"
android:toXScale="1.5"
android:toYScale="1.5" />

对应的类为ScaleAnimation,父类也是Animation,添加到View上的用法和AlphaAnimation一样,代码如下:

1
2
ScaleAnimation zoomOutAnimation = (ScaleAnimation) AnimationUtils.loadAnimation(this, R.anim.zoom_out);
view.startAnimation(zoomOutAnimation);
Set Animation

标签可以将多个动画组合起来,变成一个动画集。比如想将一张图片缩放的同时也做移动,这时候就要用set标签组合缩放动画和移动动画了。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- res/anim/move_and_scale.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="200%"
android:toYDelta="0" />
<scale
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="0%"
android:pivotY="100%"
android:toXScale="1.5"
android:toYScale="1.5" />
</set>

以上代码实现的动画效果为向右移动的同时也同步放大。Set标签在视图动画中除了可以组合Alpha ,Rotate , Scale , Scale 这四种标签,也可以嵌套其他Set标签。

Property Animation

引用官方文档

The property animation system is a robust framework that allows you to animate almost anything. You can define an animation to change any object property over time, regardless of whether it draws to the screen or not. A property animation changes a property’s (a field in an object) value over a specified length of time. To animate something, you specify the object property that you want to animate, such as an object’s position on the screen, how long you want to animate it for, and what values you want to animate between

视图动画只能作用于View,而且视图动画改变的只是View的绘制效果,View真正的属性并没有改变。比如,一个按钮做平移的动画,虽然按钮的确做了平移,但按钮可点击的区域并没随着平移而改变,还是在原来的位置。而属性动画则可以改变真正的属性,从而实现按钮平移时点击区域也跟着平移。通俗点说,属性动画其实就是在一定时间内,按照一定规律来改变对象的属性,从而使对象展现出动画效果。

属性动画是在android 3.0引入的动画体系,如果还想适配基本已经灭绝的2.x版本,只好绕道了。
属性动画和视图动画一样,可以通过xml文件定义,不同的是,视图动画的xml文件放于res/anim/目录下,而属性动画的xml文件则放于res/animator/目录下。一个是anim,一个是animator,别搞错了。同样的,在Java代码里引用属性动画的xml文件时,则用R.animator.filename,不同于视图动画,引用时为R.anim.filename

属性动画主要有三个元素:animator,objectAnimator,set

相对应的有三个类:ValueAnimatorObjectAnimatorAnimatorSet
ValueAnimator是基本的动画类,处理值动画,通过监听某一值的变化,进行相应的操作。ObjectAnimatorValueAnimator的子类,处理对象动画。AnimatorSet则为动画集,可以组合另外两种动画或动画集。相应的三个标签元素的关系也一样。
样式开发主要还是用xml的形式,所以这里先主要还是讲标签的用法。

animator

animator标签与对应的ValueAnimator类提供了属性动画的核心功能,包括计算动画值、动画时间细节、是否重复等。执行属性动画分两个步骤:

  1. 计算动画值
  2. 将动画值应用到对象和属性上

ValuAnimiator只完成第一步,即只计算值,要实现第二步则需要在值变化的监听器里自行更新对象属性。
通过animator标签可以很方便的对ValuAnimiator进行设置,可设置的属性如下:

  • android:duration 动画从开始到结束持续的时长,单位为毫秒
  • android:startOffset 设置动画执行之前的等待时长,单位为毫秒
  • android:repeatCount 设置动画重复执行的次数,默认为0,即不重复;可设为-1或infinite,表示无限重复
  • android:repeatMode 设置动画重复执行的模式,可设为以下两个值其中之一:
    • restart 动画重复执行时从起点开始,默认为该值
    • reverse 动画会反方向执行
  • android:valueFrom 动画开始的值,可以为int值、float值或color值
  • android:valueTo 动画结束的值,可以为int值、float值或color值
  • android:valueType 动画值类型,若为color值,则无需设置该属性
    • intType 指定动画值,即以上两个value属性的值为整型
    • floatType 指定动画值,即以上两个value属性的值为浮点型,默认值
  • android:interpolator 设置动画速率的变化,比如加速、减速、匀速等,需要指定Interpolator资源。

接着,用一个实例讲解具体的用法吧。在这个例子里,将一个按钮的宽度进行缩放,从100%缩放到20%。
xml文件的代码如下:

1
2
3
4
5
6
7
<!-- res/animator/value_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />

可看到,值的变化从100到20,动画时长3000毫秒,以下则是目标按钮的xml代码:

1
2
3
4
5
6
7
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_btn_normal"
android:onClick="onScaleWidth"
android:text="点我"
android:textColor="@android:color/white" />

按钮默认是填充屏幕宽度的,点击时的执行方法为onScaleWidth,以下则是onScaleWidth方法的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void onScaleWidth(final View view) {
// 获取屏幕宽度
final int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.value_animator);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
// 当前动画值,即为当前宽度比例值
int currentValue = (Integer) animator.getAnimatedValue();
// 根据比例更改目标view的宽度
view.getLayoutParams().width = maxWidth * currentValue / 100;
view.requestLayout();
}
});
valueAnimator.start();
}

从中,我们可以看到属性动画则是通过AnimatorInflater类的loadAnimation()方法获取相应的Animator类实例。另外,ValueAnimator通过添加AnimatorUpdateListener监听器监听值的变化,从而再手动更新目标对象的属性。最后,通过调用**valueAnimator.start()**方法启动动画。

objectAnimator

objectAnimator标签对应的类为ObjectAnimator,为ValueAnimator的子类。objectAnimator标签与标签animator不同的是,可以直接指定动画的目标对象的属性。标签可设置的属性除了和一样的那些,另外多了一个:

  • android:propertyName 目标对象的属性名,要求目标对象必须提供该属性的setter方法,如果动画的时候没有初始值,还需要提供getter方法

还是用实例说明具体用法,还是用上面的例子,将一个按钮的宽度进行缩放,从100%缩放到20%,但这次改用objectAnimator实现。

1
2
3
4
5
6
7
8
<!-- res/animator/object_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:propertyName="width"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />

与上面animator标签方式做对比,就只是多了一个android:propertyName的属性,设置值为width。也就是说,动画改变的属性为width,值将从100逐渐减到20。另外,值是从setWidth()传递过去的,再从getWidth()获取。而且,这里设置的值代表的是比例值,因此,还需要进行计算转化为实际的宽度值。最后,对象实际的宽度值为view.getLayoutParams().width。因此,需要用一个包装类来包装原始的view对象,对其提供setWidth()和getWidth()方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static class ViewWrapper {
private View target; //目标对象
private int maxWidth; //最长宽度值

public ViewWrapper(View target, int maxWidth) {
this.target = target;
this.maxWidth = maxWidth;
}

public int getWidth() {
return target.getLayoutParams().width;
}

public void setWidth(int widthValue) {
//widthValue的值从100到20变化
target.getLayoutParams().width = maxWidth * widthValue / 100;
target.requestLayout();
}
}

上面setWidth()的代码里,根据比例值转化为了实际的宽度值。最后,动画处理的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
public void onScaleWidth(View view) {
// 获取屏幕宽度
int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
// 将目标view进行包装
ViewWrapper wrapper = new ViewWrapper(view, maxWidth);
// 将xml转化为ObjectAnimator对象
ObjectAnimator objectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.object_animator);
// 设置动画的目标对象为包装后的view
objectAnimator.setTarget(wrapper);
// 启动动画
objectAnimator.start();
}

ObjectAnimator提供了属性的设置,但相应的需要有该属性的setter和getter方法。而ValueAnimator则只是定义了值的变化,并不指定目标属性,所以也不需要提供setter和getter方法,但只能在AnimatorUpdateListener监听器里手动更新属性。不过,也因为没有指定属性,所以其实更具灵活性了,你可以在监听器里根据值的变化做任何事情,比如更新多个属性,比如在缩放宽度的同时做垂直移动。

为了对View更方便的设置属性动画,Android系统也提供了View的一些属性和相应的setter和getter方法:

  • alpha:透明度,默认为1,表示不透明,0表示完全透明
  • pivotX 和 pivotY:旋转的轴点和缩放的基准点,默认是View的中心点
  • scaleX 和 scaleY:基于pivotX和pivotY的缩放,1表示无缩放,小于1表示收缩,大于1则放大
  • rotationrotationX 和 rotationY:基于轴点(pivotX和pivotY)的旋转,rotation为平面的旋转,rotationX和rotationY为立体的旋转
  • translationX 和 translationY:View的屏幕位置坐标变化量,以layout容器的左上角为坐标原点
  • x 和 y:View在父容器内的最终位置,是左上角坐标和偏移量(translationX,translationY)的和
set

set标签对应于AnimatorSet类,可以将多个动画组合成一个动画集,如上面提到的在缩放宽度的同时做垂直移动,可以将一个缩放宽度的动画和一个垂直移动的动画组合在一起。
set标签有一个属性可以设置动画的时序关系:

  • android:ordering 设置动画的时序关系,取值可为以下两个值之一:together 动画同时执行,默认值sequentially 动画按顺序执行

那如果想有些动画同时执行,有些按顺序执行,该怎么办呢?因为set标签是可以嵌套其他set标签的,也就是说可以将同时执行的组合在一个set标签,再嵌在按顺序执行的set标签内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- res/animator/animator_set.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
<objectAnimator
android:duration="3000"
android:propertyName="width"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />
<objectAnimator
android:duration="3000"
android:propertyName="marginTop"
android:valueFrom="0"
android:valueTo="100"
android:valueType="intType" />
</set>

以上代码可实现两个同时执行的动画,一个将width从100缩放到20,一个将marginTop从0增加到100。多了一个marginTop属性,那么,在ViewWrapper添加setMarginTop()方法,添加后的ViewWrapper类代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static class ViewWrapper {
private View target;
private int maxWidth;

public ViewWrapper(View target, int maxWidth) {
this.target = target;
this.maxWidth = maxWidth;
}

public int getWidth() {
return target.getLayoutParams().width;
}

public void setWidth(int widthValue) {
target.getLayoutParams().width = maxWidth * widthValue / 100;
target.requestLayout();
}

public void setMarginTop(int margin) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) target.getLayoutParams();
layoutParams.setMargins(0, margin, 0, 0);
target.setLayoutParams(layoutParams);
}
}

最后,动画处理的代码:

1
2
3
4
5
6
7
8
9
10
11
12
public void onScaleWidth(View view) {
// 获取屏幕宽度
int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
// 将目标view进行包装
ViewWrapper wrapper = new ViewWrapper(view, maxWidth);
// 将xml转化为ObjectAnimator对象
AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.animator_set);
// 设置动画的目标对象为包装后的view
animatorSet.setTarget(wrapper);
// 启动动画
animatorSet.start();
}

这样就实现了宽度缩放和垂直移动的效果。

当然,在Android3.0之后,Google给View增加了animate方法来直接驱动属性动画,代码如下:

1
view.animate().x(50f).y(100f);

小结

目前到此为止,我们了解到在Android中有三种不同的动画类型,在Android3.0版本其实是一个区分点,在3.0之前用的居多都是View Animation,而在3.0之后用的居多是Property Animation,本次介绍的是这三者之间的区别和不同用法,下篇介绍具体实例和自定义动画方面的知识点,敬请期待。

,