Android动画

动画分类

  • View动画
  • 帧动画(也属于View动画的一种)
  • 属性动画

View动画

简介

View动画是一种渐进式动画,定义动画开始和结束的两帧,并指定动画变化的时间和方式。通过平移、缩放、旋转和透明度四种效果结合成复杂的动画效果。(在开始和结束帧之间插入的渐变值依据的是插值器)

分类

名称 标签 子类 效果
平移动画 TranslateAnimation 移动View
缩放动画 ScaleAnimation 放缩View
旋转动画 RotateAnimation 旋转View
透明度动画 AlphaAnimation 改变View的透明度

使用

这四种动画既可以通过XML定义,也可以通过代码来动态创建。建议采用XML来定义View动画,因为可读性更好。

通过XML创建动画

  1. 要先在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>
  1. 创建好xml文件后,在代码中使用:

例如给mImage控件添加渐变动画

1
2
Animation alpha = AnimationUtils.loadAnimation(this, R.anim.alpha);
mImage.startAnimation(alpha);

其他几种动画的用法与之类似

通过代码创建动画

给控件设置平移动画,操作如下:

1
2
3
TranslateAnimation 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
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<!-- android:oneshot属性:false表示循环播放,true表示只播放一次(停在最后一帧,
但其实没有停止动画,需要调用AnimationDrawable的stop方法停止) -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@drawable/pic_two_before" android:duration="500"/>
<item android:drawable="@drawable/pic_two_after" android:duration="500"/>
<item android:drawable="@drawable/pic_two_before" android:duration="500"/>
<item android:drawable="@drawable/pic_two_after" android:duration="500"/>
</animation-list>
  • 然后在主活动中获得该drawable的AnimationDrawable实例,并开始动画,代码如下
1
2
3
4
5
6
7
8
mFrameImage.setBackgroundResource(R.drawable.frame_animation);
//也可在xml文件中直接设置Background属性
AnimationDrawable animationDrawable = (AnimationDrawable) mFrameImage.getBackground();
//获得该控件背景的AnimationDrawable对象
if (animationDrawable.isRunning()) {
animationDrawable.stop(); //如果动画正在运行则停止动画
}
animationDrawable.start(); //开始动画

注意

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

View动画的特殊使用场景

LayoutAnimation

LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时都会具有这种效果。这种效果常常用于ListView上。

使用

  1. 定义LayoutAnimation,如下所示

anim\layout_animation.xml

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<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.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<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

    为子元素指定具体的入场动画。

  1. 为ViewGroup指定android:layoutAnimation属性
1
2
3
4
5
<ListView
android:id="@+id/lv_layout_animation_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutAnimation="@anim/layout_animation"/>

除了该种方式外,还可以通过在代码中通过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)

可以看出,第一个是指定要跳转活动的动画,第二个是当前活动。

使用

  1. 先定义好出场和入场的动画

出场动画 tran_out.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<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.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<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. 跳转活动时编写如下代码
1
2
startActivity(new Intent(MainActivity.this, SecondActivity.class));
overridePendingTransition(R.anim.tran_in, R.anim.tran_out); //加入自定义的切换动画

属性动画

  • 属性动画和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
2
3
/*
* @param propertyName The name of the property being animated.
*/

从官方的注释可以看到,第二个参数是一个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
15
AnimatorSet 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
14
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.setDuration(10*1000);
valueAnimator.setInterpolator(new LinearInterpolator()); //匀速播放动画
//添加动画监听,手动改变对象的属性值(属性值发生改变就会调用该方法)
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
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
2
3
4
5
6
ViewWrapper wrapper = new ViewWrapper(mTestButton);     //对button进行包装
int buttonWidth = mTestButton.getMeasuredWidth(); //获取button宽度
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(wrapper,
"width", buttonWidth, 500, buttonWidth);
//改变button的宽度(先变为500,后恢复)
objectAnimator.setDuration(3000).start();

可以看到,例子中用了一个包装类。因为Button类并没有提供相应的setWidth和getWidth方法,所以我们需要做相应处理。这时有两种方法:

  • 给该对象加上set和get方法(这里的Button类是安卓SDK实现的,我们不能直接加上set和get方法,除非新建一个类继承Button再重写set和get方法)
  • 用一个类包装原始对象,间接为期提供set和get方法(这里就是采用这种方法)

ViewWrapper

看看ViewWrapper是怎样包装原始对象的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ViewWrapper {
private View mTarget;

public ViewWrapper(View mTarget) {
this.mTarget = mTarget;
}

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

public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}

参考资料

-------------    本文到此结束  感谢您的阅读    -------------
0%