一、引言
android的动画可以分为3种:View动画、帧动画和属性动画。View动画就是我们比较常见的对一些图形变化(平移、缩放、旋转、透明度),并且View动画支持自定义,官方提供距离,中心点,播放时间等等方法供我们使用。帧动画通过顺序播放一系列图像从而产生动画效果,类似以前老电影那种逐帧播放的感觉。属性动画,故名思议,修改相关View的属性,我们在用View动画的时候,无论View的平移、缩放等,View原本的属性是不变的,所以,属性动画可以通过动态地改变对象的属性来达到属性效果。
那么,话不多说,先去看下基础的View动画,学会View动画能帮我们解决大部分业务场景。
要是不想看啰嗦的介绍,可以直接运行项目去熟悉。
二、View动画
平移动画 translate
缩放动画 scale
旋转动画 rotate
透明度动画 alpha
效果展示
位移动画
public void translate(View view) {
TranslateAnimation translateAnimation = new TranslateAnimation(0, mX-mTranlateX, 0, mY-mTranslateY);
translateAnimation.setDuration(2000);
// view.setAnimation(translateAnimation);
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束可以做些操作
view.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(translateAnimation);
}
透明度动画
private void alphaAnimation(){
// Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_set);
// mBtnAlpha.startAnimation(animation);
AlphaAnimation alphaAnimation = new AlphaAnimation(1,0);
alphaAnimation.setDuration(2000);
alphaAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//这边可以对整个动画过程做监听
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mBtnAlpha.startAnimation(alphaAnimation);
}
缩放动画
若要替换指定大小,我们可以先获取目标View的宽高、再根据需要放到缩小的尺寸做除,来获取比例,将参数带入
public void startScaleAnimation() {
/**
* ScaleAnimation第一种构造
*
* @param fromX X方向开始时的宽度,1f表示控件原有大小
* @param toX X方向结束时的宽度,
* @param fromY Y方向上开的宽度,
* @param toY Y方向结束的宽度
* 这里还有一个问题:缩放的中心在哪里? 使用这种构造方法,默认是左上角的位置,以左上角为中心开始缩放
*/
ScaleAnimation scaleAnimation = new ScaleAnimation(1f, 2f, 1f, 2f);
/**
* ScaleAnimation第二种构造解决了第一种构造的缺陷, 无法指定缩放的位置
*
* @param fromX 同上
* @param toX 同上
* @param fromY 同上
* @param toY 同上
* @param pivotX 缩放的轴心X的位置,取值类型是float,单位是px像素,比如:X方向控件中心位置是mIvScale.getWidth() / 2f
* @param pivotY 缩放的轴心Y的位置,取值类型是float,单位是px像素,比如:X方向控件中心位置是mIvScale.getHeight() / 2f
*/
ScaleAnimation scaleAnimation1 = new ScaleAnimation(1f, 2f, 1f, 2f, mIvImg.getWidth() / 2f, mIvImg.getHeight() / 2f);
/**
* ScaleAnimation第三种构造在第二种构造的基础上,可以通过多种方式指定轴心的位置,通过Type来约束
*
* @param fromX 同上
* @param toX 同上
* @param fromY 同上T
* @param toY 同上
* @param pivotXType 用来约束pivotXValue的取值。取值有三种:Animation.ABSOLUTE,Animation.RELATIVE_TO_SELF,Animation.RELATIVE_TO_PARENT
* Type:Animation.ABSOLUTE:绝对,如果设置这种类型,后面pivotXValue取值就必须是像素点;比如:控件X方向上的中心点,pivotXValue的取值mIvScale.getWidth() / 2f
* Animation.RELATIVE_TO_SELF:相对于控件自己,设置这种类型,后面pivotXValue取值就会去拿这个取值是乘上控件本身的宽度;比如:控件X方向上的中心点,pivotXValue的取值0.5f
* Animation.RELATIVE_TO_PARENT:相对于它父容器(这个父容器是指包括这个这个做动画控件的外一层控件), 原理同上,
* @param pivotXValue 配合pivotXType使用,原理在上面
* @param pivotYType 原理同上
* @param pivotYValue 原理同上
*/
ScaleAnimation scaleAnimation2 = new ScaleAnimation(1f, 2f, 1f, 2f, ScaleAnimation.ABSOLUTE,
mIvImg.getWidth() / 2f, ScaleAnimation.ABSOLUTE, mIvImg.getHeight() / 2f);
//设置动画持续时长
scaleAnimation2.setDuration(3000);
//设置动画结束之后的状态是否是动画的最终状态,true,表示是保持动画结束时的最终状态
scaleAnimation2.setFillAfter(true);
//设置动画结束之后的状态是否是动画开始时的状态,true,表示是保持动画开始时的状态
scaleAnimation2.setFillBefore(true);
//设置动画的重复模式:反转REVERSE和重新开始RESTART
scaleAnimation2.setRepeatMode(ScaleAnimation.REVERSE);
//设置动画播放次数
scaleAnimation2.setRepeatCount(ScaleAnimation.INFINITE);
//开始动画
mIvImg.startAnimation(scaleAnimation2);
//清除动画
// mIvImg.clearAnimation();
//同样cancel()也能取消掉动画
// scaleAnimation2.cancel();
}
旋转动画
private void rolateAnimation(View view) {
RotateAnimation animation = new RotateAnimation(0, 360f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setDuration(2000);
animation.setFillAfter(true);//动画结束是否保持动画结束的样式
view.startAnimation(animation);
}
组合动画
当我们需要将多个动画组合到一起,主要用到AnimationSet,能够让我们同时将多种动画同时处理。
private void translateWithScale(View view){
TranslateAnimation translateAnimation = new TranslateAnimation(0,200,0,0);
Animation mScaleAnimation = new ScaleAnimation(0.5f, 1.0f, 0.5f,1.0f);
AnimationSet animationSet =new AnimationSet(false);
animationSet.addAnimation(translateAnimation);
animationSet.addAnimation(mScaleAnimation);
animationSet.setDuration(4000);
animationSet.setFillAfter(true);
view.startAnimation(animationSet);
}
三、帧动画
帧动画是顺序播放一组预先定义好的图片,类似与电影播放。不同于View动画,系统提供了一个类
AnimationDrawable来使用帧动画。
1.第一步需要用xml创建一个 AnimationDrawable
<?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.第二步用上述的drawable来作为View的背景并通过Drawable来播放动画即可。
mBtnDrawable.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable) mBtnDrawable.getBackground();
drawable.start();
帧动画使用比较简单,但是涉及刀很多图片的变化,所以在使用帧动画尽量避免使用尺寸较大的图片来防止oom。
属性动画
属性动画是API 11 新加入的特性,和View动画不同,他对作用对象进行了扩展,属性动画可以对任何对象做动画,甚至还可以没有对象。属性动画在指定的时间长度内更改属性的值(对象中的字段)。要使某个对象具有动画效果,需要指定要使其具有动画效果的对象属性,例如对象在屏幕上的位置、要使其具有动画效果的时间长度以及要使其具有动画效果的值。
在讲解之前,大家是不是有疑问,有了上述动画,还需要属性动画作甚。如果给一个View只有宽度增加一倍,大家可能会想到缩放动画,缩放动画是能实现,但如果button上有文字之类的,整体动画效果并不是很好。
- Duration: 可以指定动画的持续时间。默认长度为300 ms。
- Time interpolation: 可以指定属性的值如何作为动画当前运行时间的函数计算。
- Repeat count and behavior: 您可以指定一个动画在持续时间结束时是否重复,以及重复动画的次数。您还可以指定是否希望动画反向播放。将其设置为reverse将反复播放动画,直到达到重复的次数。
- Animator sets: :可以将动画分组到逻辑集中,这些逻辑集可以一起播放,也可以按顺序播放,也可以在指定的延迟之后播放。这边我们可以做组合动画。
- Frame refresh delay: 可以指定刷新动画帧的频率。默认设置为每10毫秒刷新一次,但是您的应用程序刷新帧的速度最终取决于整个系统的繁忙程度以及系统为底层计时器提供服务的速度。
首先我们来回顾下一下动画是如何工作的,看一下下面官方给的两组图。
一、第一幅图是一个物体以匀速从做向右进行滑动,公式 y = kx,物体以恒定的速度移动。在View动画中,我们可以穿出持续时间和滑动距离来实现。想必这一种大家都能信手拈来了。
二、第二幅图是一个非线性的滑动形式,该对象在动画开始时加速,在动画结束时减速。该对象仍然在40毫秒内移动40个像素,但不是线性的。在开始时,这个动画加速到中途点,然后从中途点减速到动画结束。如图2所示,动画开始和结束的距离小于中间的距离。这组动画实际上就需要用到插值器,官方给我们预留的有 AccelerateDecelerateInterpolator(非匀速)等。通过设置插值器translateAnimation.setInterpolator去完成。通过View的动画我们也能完成。
以上都是要们调用官方给我们的api,接下来我们主要去了解下这些动画原理是怎么做的,属性动画又是如何完成伤处上述动画效果。
首先看下类图
我们可以看到我们需要属性动画需要用到的ValueAnimator,同时我们也能对整个动画做监听,首先需要理解TimeInterpolator和TypeEvaluator参数的意义。
TimeInterpolator
中文翻译为时间插值器,它的作用是根据时间流逝的百分比来计算当前属性值改变的百分比,时间内插器定义了动画的变化率。这使得动画具有非线性运动,如加速和减速。将表示动画流逝时间的值映射到所表示的值内插分数。这个内插的值然后乘以in的变化量动画的值,以获取当前动画流逝时间内的动画值。如上述两图不同的速率。
TypeEvaluator
TypeEvaluator 为类型估值算法,它的作用是根据当前属性改变的百分比来计算改变后的属性值,系统预置的有IntEvaluator(针对整型属性)、 FloatEvaluator(针对浮点型属性)和ArgbEvaluator, FloatArrayEvaluator, IntArrayEvaluator, PointFEvaluator, RectEvaluator。
到这里还未对上述这两个参数有一个很深的概念,接下来我们用上述的匀速滑动为例子看下属性动画是如何完成的。首先创建一个ValueAnimator,为其指定要设置动画的属性的开始值和结束值以及动画的持续时间。调用时start()
,动画开始。在整个动画过程中,会ValueAnimator
根据动画 的持续时间和经过的时间来计算介于0和1之间的经过分数。逝去的分数表示动画已完成的时间百分比,0表示0%,1表示100%。
由于动画的默认刷新速率为10ms/帧,例如,在图1中,由于总持续时间为t = 40 ms,在第三帧的时候,因此在t = 20 ms处经过的分数将为0.5。时间流逝的百分比为0.5(20/40),意味着时间过了一半。我们这边以线性匀速为例,那么所滑动的距离为x为总的一半。看下线性插值器的源码,看参数传入的多少返回的多少。即y=kx+b。那我们最终需要确定 y这个距离应该为多少呢。需要通过估值算法来确定。
/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
}
接下来我们看下估值算法的源码,那具体实现方式IntEvaluator为例 公式为resule = x0+t*(x1-x0); evaluate的参数分别表示估值小数、开始值和结束值,对应于我们的例子为0.5,0,40。根据上述算法,整型估值返回给我们的结果是20,这就是(x=20,t=20ms)的由来。
public interface TypeEvaluator<T> {
/**
* result = x0 + t * (x1 - x0)
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value.
* @param endValue The end value.
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);
}
public class IntEvaluator implements TypeEvaluator<Integer> {
/**
* result = x0 + t * (v1 - v0)
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>int</code> or
* <code>Integer</code>
* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
同样的我们也可以自己实现相关接口来实现我们自己对应的业务场景动画。
例子:比如我们要动态改变某个按钮的大小
ObjectAnimator.ofInt(mBtnWidthChange, "width", 500).setDuration(5000).start();
小结 属性动画和视图动画不同点
1.属性动画扩展性更强。视图动画系统提供仅对View
对象进行动画处理的功能,因此,如果要对非View
对象进行动画处理,则必须实现自己的代码。视图动画系统还受到限制,因为它仅公开了View
对象的一些方面以进行动画处理,例如视图的缩放和旋转,而不是背景颜色。
2.属性动画改变了View实际属性,而不只是视图所表现的样式,视图动画系统的另一个缺点是它仅在绘制视图的位置进行修改,而不是实际的视图本身。例如,如果为按钮设置了动画以在屏幕上移动,则该按钮会正确绘制,但是可以单击该按钮的实际位置不会更改,因此您必须实现自己的逻辑来进行处理。属性动画系统,可以完全消除这些约束,并且可以为任何对象(视图和非视图)的任何属性设置动画,并且实际上可以修改对象本身。属性动画系统在执行动画方面也更加强大
如何使用组合动画 ----- AnimatorSet
在许多情况下,您想播放取决于另一个动画何时开始或结束的动画。Android系统可让您将动画捆绑到中AnimatorSet
,从而可以指定是同时,顺序还是在指定的延迟后开始动画。您还可以AnimatorSet
在彼此之间嵌套对象。