android动画中的常用的三种动画,即逐帧动画(FrameAnimation),补间动画(TweenAnimation)和属性动画(PropertyAniation).
首先就最简单的FramAnimation入手,看看如何分别用代码和xml文件实现。
如果是将动画定义在xml文件中,无论是哪一种动画,只要定义在xml文件中,我们都需要去在res目录下创建一个anima的folder用于存储和读取定义的xml文件。
FrameAnimation的资源类型为drawable,标签类型为animation-list(ps:套图自己找,名字自己取)
<?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/f1" android:duration="300" />
<item android:drawable="@drawable/f2" android:duration="300" />
<item android:drawable="@drawable/f3" android:duration="300" />
<item android:drawable="@drawable/f4" android:duration="300" />
</animation-list>
oneshot的值有两个true/false,表示动画是否重复。
至此xml中的部分就完成了,只需要在代码中初始化某一控件后(多为ImageView)添加设置动画就可以了
image = (ImageView) findViewById(R.id.frame_image);
image.setBackgroundResource(R.anim.frame);
AnimationDrawable anim = (AnimationDrawable) image.getBackground();
anim.start();
以上是设置ImageView的背景,当然也可以直接设置成图片资源,效果都是一样的。 image.setImageResource(R.drawable.animation2);
animationDrawable = (AnimationDrawable) animationIV.getDrawable();
animationDrawable.start();
此时结束,动画还是没能显示,出现这种现象是因为当我们在onCreate中调用AnimationDrawable的start方法时,窗口Window对象
还没有完全初始化,AnimationDrawable不能完全追加到窗口Window对象中,那么该怎么办呢?我们需要把这段代码放在
onWindowFocusChanged方法中,当Activity展示给用户时,onWindowFocusChanged方法就会被调用,我们正是在这个时候实现
我们的动画效果。当然,onWindowFocusChanged是在onCreate之后被调用的。当然如果是将代码写入一个点击监听或者触摸监听
内,就没这么多事了。
接下来看一看如何用代码进行逐帧动画的实现,其实也很简单
AnimationDrawable ad = new AnimationDrawable();
for (int i = 1; i <= 4; i++) {
int resourcesId = getResources().getIdentifier(imageName(图片的名字), "drawable", getPackageName());
Drawable drawable = getResources().getDrawable(resourceId);
anim.addFrame(drawable, 300);
}
anim.setOneShot(false);
image.setBackgroundDrawable(anim);
或者:image.setImageResource(anim);
anim.start();
接下来我们来看一看补间动画(TweenAnimation).
补间动画又叫做View Animation,正如其名一样,它只能对View和继承View的控件视图进行操作,而且操作也是视图上的,是一系
列View形状的变换,如大小的缩放,透明度的改变,位置的改变,真实的属性是不会发生改变的,各个动画效果之间可以连用,但
不会有影响,关于这一点,讲到属性动画是会进行对比的。
首先我们来看一下,如何使用xml文件进行补间动画的设置。
资源标签为<TweenAnimaton>元素标签有五个,分别是<alpha>,<translation>,<rotate>,<scale>和<set>.
AlphaAnimation:透明度(alpha)渐变效果,对应<alpha/>标签。
TranslateAnimation:位移渐变,需要指定移动点的开始和结束坐标,对应<translate/>标签。
ScaleAnimation:缩放渐变,可以指定缩放的参考点,对应<scale/>标签。
RotateAnimation:旋转渐变,可以指定旋转的参考点,对应<rotate/>标签。
AnimationSet:组合渐变,支持组合多种渐变效果,对应<set/>标签。
可以通过startOffset属性设置 各个动画的开始偏移(开始时间)来达到动画顺序播放的效果。
可以通过设置interpolator属性改变动画渐变的方式,
如AccelerateInterpolator,开始时慢,然后逐渐加快。默认为AccelerateDecelerateInterpolator。
我从别人的博客截取了一张表,大家自己看:
博客地址:http://blog.csdn.net/qq_34381084/article/details/52228017
Interpolator的介绍
AccelerateDecelerateInterpolator | 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速 |
AccelerateInterpolator | 在动画开始的地方速率改变比较慢,然后开始加速 |
CycleInterpolator | 动画循环播放特定的次数,速率改变沿着正弦曲线 |
DecelerateInterpolator | 在动画开始的地方速率改变比较慢,然后开始减速 |
LinearInterpolator | 在动画的以均匀的速率改变 |
这里为了方便起见就一个set标签搞定了,不多说,上代码:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator"
android:shareInterpolator="true" >
<scale
android:duration="2000"
android:fromXScale="0.2"
android:fromYScale="0.2"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.5"
android:toYScale="1.5" />
<rotate
android:duration="1000"
android:fromDegrees="0"
android:repeatCount="1"
android:repeatMode="reverse"
android:toDegrees="360" />
<translate
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="320"
android:toYDelta="0" />
<alpha
android:duration="2000"
android:fromAlpha="1.0"
android:toAlpha="0.1" />
</set>
代码控件设置就ok了。
Animation a=AnimationUtil.loadAnimation(R.drawable.name);
image.startAnimation(a);
接下来是代码单纯代码设置动画,简单粗暴。
AnimationSet set = new AnimationSet(true);
AlphaAnimation alphaAnimation=new AlphaAnimation(fromAlpha, toAlpha);
set.addAnimation(alpha);
animationSet.setFillBefore(true);
animationSet.setFillAfter(false);
animationSet.setDuration(200);
ImageView.startAnimation(animationSet);
好了,最后是相对用的比较多的PropertyAnimation了。属性动画是android3.0以后推出的动画类型,他的出现弥补了补间动画的不
足之处,属性动画对于控件属性的设置更改是真实的修改,而不仅仅是对于View的绘制,如果顺序播放几个动画效果,是会互相影
响的。属性动画的作用对象不仅仅局限于View和其子类控件,就说一点,他的作用对象是object你就知道他的作用范围有多广了。
好的,菜鸟集中营,我们从头说起:
属性动画中最核心的一个类ValuAnimator
ValueAnimator:
属性动画的运行机制是通过不断地对值进行操作来实现的,
而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的.
只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,
那么ValueAnimator就会自动完成从初始值平滑地过渡到结束值这样的效果。
ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,确实是一个非常重要的类。
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(300);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (float) animation.getAnimatedValue();
Log.d("TAG", "cuurent value is " + currentValue);
}
});
anim.start();
通过log你可以清楚的看到animation值的变化过程。
ofFloat()方法当中允许传入多个float类型的参数,这里传入0和1就表示将值从0平滑过渡到1,
然后调用ValueAnimator的setDuration()方法来设置动画运行的时长,最后调用start()方法启动动画。
借助监听器可以记录属性动画的运行效果。
当然也许你并不需要小数位数的动画过渡,可能你只是希望将一个整数值从0平滑地过渡到100,
那么也很简单,只需要调用ValueAnimator的ofInt()方法就可以了
ValueAnimator anim = ValueAnimator.ofInt(0, 100);
可以调用setStartDelay()方法来设置动画延迟播放的时间
,
调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,
循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思
ObjectAnimator:可以直接对任意对象的任意属性进行动画操作
ObjectAnimator会更加常用一些,但是它其实是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
animator.setDuration(5000);
animator.start();
将一个TextView在5秒中内从常规变换成全透明,再从全透明变换成常规
这里第一个参数要求传入一个object对象,想要对哪个对象进行动画操作就传入什么,这里传入了一个textview。
第二个参数是想要对该对象的哪个属性进行动画操作,改变TextView的不透明度,因此这里传入"alpha"。
后面的参数就是不固定长度了,想要完成什么样的动画就传入什么值,
这里传入的值就表示将TextView从常规变换成全透明,再从全透明变换成常规。
之后调用setDuration()方法来设置动画的时长,然后调用start()方法启动动画
ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法
组合动画
实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,
如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,
AnimatorSet.Builder中包括以下四个方法:
after(Animator anim) 将现有动画插入到传入的动画之后执行
after(long delay) 将现有动画延迟指定毫秒后执行
before(Animator anim) 将现有动画插入到传入的动画之前执行
with(Animator anim) 将现有动画和传入的动画同时执行
Animator监听器-----AnimatorListener();
监听到动画的各种事件,比如动画何时开始,何时结束,然后在开始或者结束的时候去执行一些逻辑处理
Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,
我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了。
anim.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
});
onAnimationStart()方法会在动画开始的时候调用,
onAnimationRepeat()方法会在动画重复执行的时候调用,
onAnimationEnd()方法会在动画结束的时候调用,
onAnimationCancel()方法会在动画被取消的时候调用。
Android提供了一个适配器类,叫作AnimatorListenerAdapter
anim.addListener(new AnimatorListenerAdapter() {
});
那么如果我想监听动画结束这个事件,就只需要单独重写这一个方法就可以了
使用XML编写动画:通过XML来完成和代码一样的属性动画功能
通过XML来编写动画可能会比通过代码来编写动画要慢一些,但是在重用方面将会变得非常轻松
然后在XML文件中的propertyAnimation资源标签下,我们一共可以使用如下三种标签:
<animator> 对应代码中的ValueAnimator
<objectAnimator> 对应代码中的ObjectAnimator
<set> 对应代码中的AnimatorSet
比如说我们想要实现一个从0到100平滑过渡的动画,在XML当中就可以这样写:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="100"
android:valueType="intType"/>
而如果我们想将一个视图的alpha属性从1变成0,就可以这样写:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="alpha"/>
最后在代码中添加设置控件代码和补间动画一样,就不在重复了。
最后是几个注意事项:
alpha动画的从无到有是(0f,1f)
scale缩放分为X轴和Y轴,值的大小设置与alpha类似
rotate为翻转,参数值为度数,所以如果设置的和alpha和scale相似就看不出什么效果了
translation 为平移,分为X轴和Y轴,,参数为正向下移动,参数未负向上移动,代码中参数的设置为px,同样小了看不出效果
动画是可以组合的,如果scale设置的参数为(1f,0f);那么后面的动画就看不出任何效果了,应为属性在scale这里就缩没有了。
最后贴一段onClickListener,去掉中间的break,你会发现神奇的效果。
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.bt_alpha:
ObjectAnimator al=ObjectAnimator.ofFloat(tv, "alpha", 0f,1f,0f,1f);
al.setDuration(2000);
al.start();
case R.id.bt_scaleX:
ObjectAnimator sx=ObjectAnimator.ofFloat(tv, "scaleX", 0f,2f,4f,1f);
sx.setDuration(2000);
sx.start();
case R.id.bt_scaleY:
ObjectAnimator sy=ObjectAnimator.ofFloat(tv, "scaleY", 0f,2f,4f,1f);
sy.setDuration(2000);
sy.start();
case R.id.bt_translationX:
ObjectAnimator tx=ObjectAnimator.ofFloat(tv, "translationX", 0f,100f,-200f,0f);
tx.setDuration(2000);
tx.start();
case R.id.bt_translationY:
ObjectAnimator ty=ObjectAnimator.ofFloat(tv, "translationY", 0f,100f,-200f,0f);
ty.setDuration(2000);
ty.start();
//沿着中心转---tween中是左上角转默认情况不一样
case R.id.bt_rotation:
ObjectAnimator ro=ObjectAnimator.ofFloat(tv, "rotation", 0f,360f,-360f,0f);
ro.setDuration(2000);
ro.start();
break;
case R.id.bt_other:
break;
}
}