动画分类
- View动画
- 帧动画(也属于View动画的一种)
- 属性动画
View动画
简介
View动画是一种渐进式动画,定义动画开始和结束的两帧,并指定动画变化的时间和方式。通过平移、缩放、旋转和透明度四种效果结合成复杂的动画效果。(在开始和结束帧之间插入的渐变值依据的是插值器)
分类
名称 | 标签 | 子类 | 效果 |
---|---|---|---|
平移动画 | TranslateAnimation | 移动View | |
缩放动画 | ScaleAnimation | 放缩View | |
旋转动画 | RotateAnimation | 旋转View | |
透明度动画 | AlphaAnimation | 改变View的透明度 |
使用
这四种动画既可以通过XML定义,也可以通过代码来动态创建。建议采用XML来定义View动画,因为可读性更好。
通过XML创建动画
- 要先在res目录下创建anim目录,之后再该目录下创建相应动画的xml文件
平移动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14<!-- fromXDelta,fromYDelta表示动画开始时的x,y坐标 -->
<!-- toXDelta,toYDelta表示动画结束的x,y坐标,这里需要注意的是他可以设置三种类型的值
(1) 50: 表示50个像素,是绝对值
(2) 50%: 表示自身大小一半,记住这里是可以设置%号的,他等于0.5,如果设置0.5表示绝对值0.5
(3) 50%p :表示父元素大小的一半,p表示相对于父元素 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="100"
android:toYDelta="100"
/>
</set>渐变动画
1
2
3
4
5
6
7
8
9
10<!-- fromAlpha表示动画开始的透明度,toAlpha表示动画结束的透明度,1.0表示完全不透明,0.0便是完全透明,两个都是浮点数 -->
<!-- startOffset表示动画开始的延迟时间,这里表示点击开始1秒后开始执行,单位是毫秒 -->
<!-- duration表示动画的持续时间,单位为毫秒,2000毫秒表示2秒 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:startOffset="1000"
android:duration="2000">
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>缩放动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!-- fromXScale,fromYScale动画开始时x,y的缩放大小 -->
<!-- toXScale,toXScale:动画结束的x,y缩放大小 -->
<!-- pivotX,pointY:缩放的中心坐标位置,与平移动画设置类似
50: 表示50个像素,是绝对值
50%: 表示自身中心位置
50%p :表示父元素的中心位置,p表示相对于父元素-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000">
<scale
android:fromXScale="1"
android:fromYScale="1"
android:toXScale="2"
android:toYScale="2"
android:pivotX="50%"
android:pivotY="50%"
/>
</set>旋转动画
1
2
3
4
5
6
7
8
9
10
11<!-- fromDegrees表示动画开始的角度,toDegrees表示动画结束的角度,正数为顺时针,负数为逆时针 -->
<!-- pivotX,pointY:旋转的中心坐标位置,设置的方式与缩放相似 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="5000"
android:interpolator="@android:anim/anticipate_interpolator">
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%" />
</set>
- 创建好xml文件后,在代码中使用:
例如给mImage控件添加渐变动画1
2Animation alpha = AnimationUtils.loadAnimation(this, R.anim.alpha);
mImage.startAnimation(alpha);
其他几种动画的用法与之类似
通过代码创建动画
给控件设置平移动画,操作如下:1
2
3TranslateAnimation translate = new TranslateAnimation(0, 100, 0, 100);
translate.setDuration(3000);
mImage.startAnimation(translate);
其他几种动画类似,分别使用AlphaAnimation、ScaleAnimation、RotateAnimation创建动画。
XML属性
标签中的属性
android:interpolator
表示动画集合所采用的插值器,插值器影响动画的速度。如果不指定该属性,默认为”@android:anim/accelerate_decelerate_interpolator”,即先加速后减速。
具体如下:(参考资料)
android:interpolator=”@android:anim/accelerate_interpolator” 设置动画为加速动画(动画播放中越来越快)
android:interpolator=”@android:anim/decelerate_interpolator” 设置动画为减速动画(动画播放中越来越慢)
android:interpolator=”@android:anim/accelerate_decelerate_interpolator” 设置动画为先加速在减速(开始速度最快 逐渐减慢)
android:interpolator=”@android:anim/anticipate_interpolator” 先反向执行一段,然后再加速反向回来(相当于我们弹簧,先反向压缩一小段,然后在加速弹出)
android:interpolator=”@android:anim/anticipate_overshoot_interpolator” 同上先反向一段,然后加速反向回来,执行完毕自带回弹效果(更形象的弹簧效果)
android:interpolator=”@android:anim/bounce_interpolator” 执行完毕之后会回弹跳跃几段(相当于我们高空掉下一颗皮球,到地面是会跳动几下)
android:interpolator=”@android:anim/cycle_interpolator” 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 mCycles Math.PI* input)
android:interpolator=”@android:anim/linear_interpolator” 线性均匀改变
android:interpolator=”@android:anim/overshoot_interpolator” 加速执行,结束之后回弹
android:shareInterpolator
表示集合中的动画是共享该插值器,默认为true,如果设置为false,那么子动画就不会拥有集合插值器的效果,如果需要插值器就要自己单独指定。
帧动画
帧动画是顺序播放一组预先定义好的图片
使用
- 需要通过XML来定义一组图片(在drawable文件夹新建一个xml文件,如图)
frame_animation.xml
1 |
|
- 然后在主活动中获得该drawable的AnimationDrawable实例,并开始动画,代码如下
1 | mFrameImage.setBackgroundResource(R.drawable.frame_animation); |
注意
- 帧动画比较容易引起OOM,所以在使用帧动画的时候应该尽量避免使用尺寸较大的图片
View动画的特殊使用场景
LayoutAnimation
LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时都会具有这种效果。这种效果常常用于ListView上。
使用
- 定义LayoutAnimation,如下所示
anim\layout_animation.xml1
2
3
4
5
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.1"
android:animationOrder="normal"
android:animation="@anim/anim_item"/>
anim\anim_item.xml1
2
3
4
5
6
7
8
9
10
11
12
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:interpolator="@android:anim/accelerate_interpolator"
android:shareInterpolator="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
<translate
android:fromXDelta="500"
android:toXDelta="0"/>
</set>
再介绍一下layoutAnimation的几个属性:
- android:delay
表示子元素开始动画时间的延迟。比如子元素入场动画的时间周期为1000ms,那么0.1表示每个子元素都需要延迟100ms才播放入场动画。总体来说,第一个子元素延迟100ms,第二个子元素延迟200ms,以此类推。
- android:animationOrder
表示子元素动画的顺序,有三种选项:normal、reverse和random。normal表示顺序显示,reverse表示逆序显示,即排在后面的子元素先开始播放入场动画,random表示随机播放入场动画。
- android:animation
为子元素指定具体的入场动画。
- 为ViewGroup指定android:layoutAnimation属性
1 | <ListView |
除了该种方式外,还可以通过在代码中通过LayoutAnimatorController来实现。
Activity的切换效果
我们可以自定义Activity的切换效果,主要用到overridePendingTransition方法,这个方法必须在startActivity或者finish方法之后调用才能生效。
该方法有两个参数,含义如下1
2
3
4
5
6
7/*
* @param enterAnim A resource ID of the animation resource to use for
* the incoming activity. Use 0 for no animation.
* @param exitAnim A resource ID of the animation resource to use for
* the outgoing activity. Use 0 for no animation.
*/
public void overridePendingTransition(int enterAnim, int exitAnim)
可以看出,第一个是指定要跳转活动的动画,第二个是当前活动。
使用
- 先定义好出场和入场的动画
出场动画 tran_out.xml1
2
3
4
5
6
7
8
9
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="0"/>
</set>
入场动画 tran_in.xml1
2
3
4
5
6
7
8
9
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000">
<translate
android:fromXDelta="100%p"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="0"/>
</set>
- 跳转活动时编写如下代码
1 | startActivity(new Intent(MainActivity.this, SecondActivity.class)); |
属性动画
- 属性动画和View动画不同,它可以对任何对象做动画,甚至可以没有对象。它改变的不仅仅是View的视觉效果,而是直接改变对象的属性。
- 属性动画主要借助ObjectAnimator、ValueAnimator和AnimatorSet。
使用(代码实现)
实际开发中,建议使用Java代码实现属性动画:因为很多时候属性的起始值是无法提前确定的(无法使用XML设置),这就需要在Java代码里动态获取。
ObjectAnimator的使用
以下这个例子可以改变对象的透明度1
2
3
4
5
6
7//ObjectAnimator的典型使用:改变控件(对象)的透明度(从不透明到透明又变回不透明)
ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mPicOneImage,
"alpha", 1.0f, 0.0f, 1.0f);
alphaAnimator.setDuration(3000);
alphaAnimator.setRepeatCount(2); //重复两次,实际上是执行三次
//设置动画重复次数,ValueAnimator.INFINITE为无限重复
alphaAnimator.start();
除了透明度外,View动画可以实现的效果属性动画都可以实现,只要改变ofFloat方法的第二个参数,并在第三个参数传入要改变的属性值就可以了。
ofFloat方法的第二个参数:属性名
1 | /* |
从官方的注释可以看到,第二个参数是一个String类型的属性名,该属性名表示将要进行动画改变的对象属性。
官方已经定义好了一些属性,具体如下
属性名 | 作用 |
---|---|
alpha | 改变透明度,1为不透明,0为透明 |
translationX | 下个位置大于上个位置时,向右移动,反之向左移动 |
translationY | 下个位置大于上个位置时,向下移动,反之向上移动 |
rotation | 下个度数大于上个度数,顺时针旋转;反之为逆时针旋转 |
rotationX | 以X轴为轴进行旋转 |
rotationY | 以y轴为轴进行旋转 |
scaleX | 沿x轴缩放,1表示原来大小,2表示两倍大小,以此类推 |
scaleY | 沿y轴缩放,1表示原来大小,2表示两倍大小,以此类推 |
AnimatorSet的使用
如果想要同时改变多个属性,可以使用AnimatorSet,示例如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(mPicOneImage,
"scaleX", 1f, 2f, 1f);
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(mPicOneImage,
"scaleY", 1f, 2f, 1f);
ObjectAnimator translationXAnimator = ObjectAnimator.ofFloat(mPicOneImage,
"translationX", 0f, 300f, 0f);
ObjectAnimator translationYAnimator = ObjectAnimator.ofFloat(mPicOneImage,
"translationY", 0f, -300f, 0f);
//同时沿x、y轴缩放,该操作在沿Y轴向下之后,在沿X轴向右之前
animatorSet.play(scaleXAnimator).with(scaleYAnimator).before(translationXAnimator)
.after(translationYAnimator);
//都设置3s
animatorSet.setDuration(3000);
animatorSet.start();
ValueAnimator的使用
ValueAnimator需要手动进行属性的更新,下面以一个计数器为例:1
2
3
4
5
6
7
8
9
10
11
12
13
14ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.setDuration(10*1000);
valueAnimator.setInterpolator(new LinearInterpolator()); //匀速播放动画
//添加动画监听,手动改变对象的属性值(属性值发生改变就会调用该方法)
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
int currentCount = (int) animation.getAnimatedValue();
//获取当前的值
mCountTextView.setText(String.valueOf(currentCount));
//更新文本
}
});
valueAnimator.start(); //开始动画
其他还有ofFloat、ofObject,跟这个差不多。
插值器和估值器
- 插值器(Interpolator)———— 决定值的变化模式(加速、匀速、减速等)
- 估值器(TypeEvaluator)———— 决定值的具体变化数值(系统内置了针对整形、浮点型、颜色的估值器)
自定义估值器
ValueAnimator.ofInt和ValueAnimator.ofFloat都具备系统内置的估值器,即IntEvaluator和FloatEvaluator,但对于ValueAnimator.ofObject,并没有系统默认实现,因为对对象的动画操作复杂多样,系统无法知道如何从初始对象过度到结束对象。因此,对于ValueAnimator.ofObject,我们需自定义估值器(TypeEvaluator)来告知系统如何进行从初始对象过渡到结束对象的逻辑。
对任意属性做动画
ofFloat方法的参数还能传入什么值?
除了上面介绍的一些值,其实我们还可以传入其他任意属性值。当然,这个任意是有限制的。对于属性a
- 对象必须要提供setA方法,如果动画没有传入初始值,那么还要提供getA方法,因为系统要去取a属性的初始值。(注意:如果这个条件不满足,程序直接crash)
- 对象提供的setA方法对属性a所做的改变要有某张反映,例如带来UI的改变(如果这条不满足,程序不会crash,但动画没有效果)
示例
下面是一个改变button宽度的例子
1 | ViewWrapper wrapper = new ViewWrapper(mTestButton); //对button进行包装 |
可以看到,例子中用了一个包装类。因为Button类并没有提供相应的setWidth和getWidth方法,所以我们需要做相应处理。这时有两种方法:
- 给该对象加上set和get方法(这里的Button类是安卓SDK实现的,我们不能直接加上set和get方法,除非新建一个类继承Button再重写set和get方法)
- 用一个类包装原始对象,间接为期提供set和get方法(这里就是采用这种方法)
ViewWrapper
看看ViewWrapper是怎样包装原始对象的
1 | public class ViewWrapper { |
参考资料
- 《Android 开发艺术探索》
- 【Android】属性动画(基本用法)
- Android动画—-ValueAnimator
- Android 属性动画:这是一篇很详细的 属性动画 总结&攻略