个人觉得,一款移动应用软件,影响用户体验的很大一部分就取决于动画,Android中也是如此,因此在应用层开发中我个人也很注重动画的使用,在能够熟练使用后就想探究其原理了,因此,开这篇博客不仅仅是对动画的一个总结,也是对动画的更深入的一个探究。话不多说,开写了。
Android动画可以分为三类:Tween Animation(补间动画)、Frame Animatino(帧动画)、Property Animation(属性动画),其中,补间动画比较简单,无非就是平移、缩放、透明度渐变,这些只需要根据相关api设置好想要的结果参数即可,想要达到预期效果可能会经过不断地调试设置~0.0~,当然,如果是老手,已经熟练了,熟练到知道一个dp、sp在手机上是多少个单位以及结果图在脑海里的样子 -0-会很容易了~。本文会重点介绍补间动画和属性动画,尤其是属性动画。
一、补间动画
1. 补间动画的使用
补间动画又分为四大类,分别是:旋转动画、平移动画、缩放动画、透明度渐变动画。下面从xml开始依次介绍动画的用法和相关的属性,最后通过直接用代码实现动画来探究其原理。
(1)旋转动画RoateAnimation
在补间动画中,动画都是平面2D效果,因此旋转动画也是指逆时针或顺时针旋转,其用法无非就是设置各个属性,其各属性含义如下:
<?xml version="1.0" encoding="utf-8"?>
<!-- 旋转动画 -->
<!--
====== 属性说明 =========
formDegrees: 开始旋转前的角度
toDegrees: 旋转后的角度
pivotX: 中心点X的坐标,其单位%p表示相对父容器的坐标,%表示相对自己的坐标,没有单位只有数字则是指在屏幕上坐标的x轴坐标
pivotY: 旋转中心店Y的坐标
duration: 动画持续时间
fillAfter: 如果为true(前提是fillEnable不是false,fillEnable属性默认为true),则动画执行完毕后会保留执行后的状态
startOffset: 动画开始的延迟时间,单位是毫秒,比如如果该值为3000,那么动画就在3秒后执行
repeatCount: 动画执行的次数
repeatMode: 重复的方式,枚举类型,有两种选择"restart"表示执行完一次动画后又从头执行,"reverse"本身就是相反的意思,
它表示动画执行完动画后沿着原来旋转的方向反向旋转回来
-->
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true"
android:duration="1400"
android:fillAfter="false"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="2"
android:repeatMode="reverse"
android:toDegrees="180"
android:zAdjustment="bottom" >
</rotate>
以上的动画效果是:一个View绕其中心点顺时针旋转180°,并重复两次,在结束第一次动画后“原路返回”执行第二次动画(repeateMode属性设置为reverse的效果),动画持续时间是1.4秒,并且动画在结束后回到执行动画之前的位置(如果fillAfter设置为true,则执行动画完毕后视图保持执行后的状态)。值得注意的就是pivotX和pivotY属性的单位,如果带有%表示相对自己旋转,如假设pivotX和pivotY都是50%,则表示动画以本视图的中心点(宽/2和高/2)旋转,如果是50%p,则表示动画以该视图的父视图的中心点来旋转。如果是50,则表示相对于屏幕坐标系的绝对坐标来旋转。
xml设置动画完毕,就差在java代码中使用了,使用方法很简单,如下两行代码搞定:
Animation anima = AnimationUtils.loadAnimation(mContext, R.anim.anim_rote);
mImageView.startAnimation(anima);
这样就可以让一个ImageView旋转起来,当然这是最简单的动画效果。现在假设我们有这样一个需求,在上面的动画基础上,我们需要在动画执行完成后,让该动画在上次的动画基础上继续执行,也就是前一个动画是从0到180°旋转,下一个动画就开始从180°到360°旋转,虽然fillAfter设置为true可以做到动画完成后保存其执行后的状态,但是当动画再次执行时仍然是从0到180°执行,因此,我们需要在动画结束后保存其执行后的状态,然后让其再次执行时从上一次执行后的状态开始执行,这就需要对动画进行监听,然而,我们要想实现这个需求使用xml并不方便,因为需要动态改变其属性,所以需要在代码中创建动画,这就体现了在代码中创建动画的灵活性但是维护性差评。对上面的代码改造如下:
/**
* 旋转
* @param view
*/
public void onRote(View view){
// Animation anima = AnimationUtils.loadAnimation(mContext, R.anim.anim_rote);
mFlag++;
Animation anima = null;
if(mFlag%2==1){
mToDegrees =180;
mFromeDegrees = 0;
anima = new RotateAnimation(mFromeDegrees, mToDegrees,Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF,0.5F);
}else if(mFlag%2==0){
mToDegrees =360;
mFromeDegrees = 180;
anima = new RotateAnimation(mFromeDegrees, mToDegrees, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF,0.5F);
}
anima.setDuration(1000);
anima.setFillAfter(true);
anima.setRepeatMode(Animation.REVERSE);
anima.setRepeatCount(2);
anima.setAnimationListener(new AnimationListener());
mImageView.startAnimation(anima);
}
/**
* 动画监听
*
* @author Administrator
*
*/
@SuppressLint("NewApi")
private class AnimationListener implements
android.view.animation.Animation.AnimationListener {
@Override
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
Animation anima = AnimationUtils.loadAnimation(mContext,
R.anim.anim_rote_01);
// mImageView.startAnimation(anima);
// mImageView.postInvalidate();
// startActivity(new---activity);
Toast.makeText(mContext, "动画执行结束", Toast.LENGTH_LONG).show();
}
@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
Toast.makeText(mContext, "动画重复", Toast.LENGTH_LONG).show();
}
@Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
Toast.makeText(mContext, "动画开始了", Toast.LENGTH_LONG).show();
}
}
上面的代码创建的动画效果和xml创建的是一样的,需要注意的是,在创建Animation对象的时候,API中提供了三个构造器,三个构造器源码如下:
/**
* Constructor used when a RotateAnimation is loaded from a resource.
*
* @param context Application context to use
* @param attrs Attribute set from which to read values
*/
public RotateAnimation(Context context, AttributeSet attrs) {
//此构造器是在用AnimationUtils.load的时候被调用,我们并不会直接使用到,官方注释已经很清楚了。
}
/**
* Constructor to use when building a RotateAnimation from code.
* Default pivotX/pivotY point is (0,0).
* 这个构造器比较简单,就是一个起始角度和结束角度,一般比较少用,因为我们需要指定旋转中心点
* @param fromDegrees Rotation offset to apply at the start of the
* animation.
*
* @param toDegrees Rotation offset to apply at the end of the animation.
*/
public RotateAnimation(float fromDegrees, float toDegrees) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mPivotX = 0.0f;
mPivotY = 0.0f;
}
/**
* Constructor to use when building a RotateAnimation from code
* 这个用的也不是很多,就是在上一个构造器的基础上添加了poivotX和pivotY两个参数用于指定旋转点
* @param fromDegrees Rotation offset to apply at the start of the
* animation.
*
* @param toDegrees Rotation offset to apply at the end of the animation.
*
* @param pivotX The X coordinate of the point about which the object is
* being rotated, specified as an absolute number where 0 is the left
* edge.
* @param pivotY The Y coordinate of the point about which the object is
* being rotated, specified as an absolute number where 0 is the top
* edge.
*/
public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mPivotXType = ABSOLUTE;
mPivotYType = ABSOLUTE;
mPivotXValue = pivotX;
mPivotYValue = pivotY;
initializePivotPoint();
}
/**
* Constructor to use when building a RotateAnimation from code
* 重点就是这个构造器,是我们常用的,第三个参数是用于指定旋转的类型,有三种选择:
* 1、相对自己(通过Animation.RELATIVE_TO_SELF设置) 2、绝对旋转(相对屏幕坐标系,通过Animation.ABSOLUTE)
* 3、相对父视图。我们通过Animation.RELATIVE_TO_PARENT设置
*
* @param fromDegrees Rotation offset to apply at the start of the
* animation.
*
* @param toDegrees Rotation offset to apply at the end of the animation.
*
* @param pivotXType Specifies how pivotXValue should be interpreted. One of
* Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
* Animation.RELATIVE_TO_PARENT.
* @param pivotXValue The X coordinate of the point about which the object
* is being rotated, specified as an absolute number where 0 is the
* left edge. This value can either be an absolute number if
* pivotXType is ABSOLUTE, or a percentage (where 1.0 is 100%)
* otherwise.
* @param pivotYType Specifies how pivotYValue should be interpreted. One of
* Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
* Animation.RELATIVE_TO_PARENT.
* @param pivotYValue The Y coordinate of the point about which the object
* is being rotated, specified as an absolute number where 0 is the
* top edge. This value can either be an absolute number if
* pivotYType is ABSOLUTE, or a percentage (where 1.0 is 100%)
* otherwise.
*/
public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mPivotXValue = pivotXValue;
mPivotXType = pivotXType;
mPivotYValue = pivotYValue;
mPivotYType = pivotYType;
initializePivotPoint();
}
(2)平移动画TranslateAnimation(略)
(3)缩放动画ScaleAnimation(略)
(4)透明度渐变动画AlphaAnimation(略)
上面就以旋转动画为例,将动画的通用属性展示出来,至于平移、缩放、透明度都是一样一样的,就几个特性不一样,但是都可以见名思意的,因此在这里不再赘述。下面直接进入主题,补间动画的实现原理分析,源码分析依然是从RoateAnimation开始。
2.补间动画原理源码初窥
(1)xml文件加载过程分析
通常我们加载xml文件生成一个Animation对象时,是用过Google封装的AnimationUtils的loadAnimation()方法,那么我们就从这个方法看看一个xml文件是如何让一个View动起来的。
/**
* Loads an {@link Animation} object from a resource
*
* @param context Application context used to access resources
* @param id The resource id of the animation to load
* @return The animation object reference by the specified id
* @throws NotFoundException when the animation cannot be loaded
*/
public static Animation loadAnimation(Context context, int id)
throws NotFoundException {
//XmlResourceParser接口是继承XmlPullParser解析器和AttributeSet接口的,AttributeSet接口以前说过,就不用再说了,
//
XmlResourceParser parser = null;
try {
//这里根据id获取解析器然后调用createAnimationFormXml方法生成一个Animation对象
parser = context.getResources().getAnimation(id);
return createAnimationFromXml(context, parser);
} catch (XmlPullParserException ex) {
NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} catch (IOException ex) {
NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} finally {
if (parser != null) parser.close();
}
}
正如上面的注释那样,loadAnimation()中其实也没啥代码,就是获取一下解析器然后调用createAnimationFromXml()来生成Animation实例:
private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
//声明一个Animationd对象
Animation anim = null;
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
//再说一次,Android中对资源xml文件解析都是使用Pull解析的,这里先判断是否有下一个节点,是否结束
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
//如果是set节点(就是我们多中组合动画的SetAnimation了),就递归调用自己,直到把所有的动画解析完毕
//然后下面是一系列的判断根据alpha、scale、rotate、translate来生成不同的动画
if (name.equals("set")) {
anim = new AnimationSet(c, attrs);
createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
} else if (name.equals("alpha")) {
anim = new AlphaAnimation(c, attrs);
} else if (name.equals("scale")) {
anim = new ScaleAnimation(c, attrs);
} else if (name.equals("rotate")) {
anim = new RotateAnimation(c, attrs);
} else if (name.equals("translate")) {
anim = new TranslateAnimation(c, attrs);
} else {
throw new RuntimeException("Unknown animation name: " + parser.getName());
}
if (parent != null) {
parent.addAnimation(anim);
}
}
return anim;
}
由上面的代码可以看出来,在使用xml文件加载动画时,其本质上还是通过解析xml文件根据节点来生成相应的XXXAnimation实例,因此,如果Android项目中不用xml文件,所有布局、动画都直接用代码实现那么速度会快一些,但是也得不偿失,因为代码布局很生硬啊!一个项目得多长时间呢。好了,接下来就看看RoateAnimation中是怎么实现动画的吧!
首先看下这个类中定义的属性字段,不多,所以全贴出来:
private float mFromDegrees;
private float mToDegrees;
//注意这里,看到,默认的mPivotXType是绝对的,就是相对于坐标系的,这个ABSLUTE是在RoateAnimation的父类Animation中
//定义的
private int mPivotXType = ABSOLUTE;
private int mPivotYType = ABSOLUTE;
private float mPivotXValue = 0.0f;
private float mPivotYValue = 0.0f;
private float mPivotX;
private float mPivotY;
属性字段就那么几个,各个字段的含义上面的xml文件都有说道,下面看下加载xml文件解析后使用到的RoateAnimation(Context context, AttributeSet attrs)构造器:
public RotateAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
//获取类型数组,就是xml中属性值得类型,int、float等
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.RotateAnimation);
//获取到旋转后的位置角度以及开始的角度
mFromDegrees = a.getFloat(
com.android.internal.R.styleable.RotateAnimation_fromDegrees, 0.0f);
mToDegrees = a.getFloat(com.android.internal.R.styleable.RotateAnimation_toDegrees, 0.0f);
//这个不用看,就封装了旋转类型和旋转角度的一系列转换
Description d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.RotateAnimation_pivotX));
mPivotXType = d.type;
mPivotXValue = d.value;
d = Description.parseValue(a.peekValue(
com.android.internal.R.styleable.RotateAnimation_pivotY));
mPivotYType = d.type;
mPivotYValue = d.value;
a.recycle();
//这个方法只有当mPivotXType为ABSOLUTE时才会起作用
initializePivotPoint();
}
上面的代码几乎就是RoateAnimation所有的代码了,还有几个构造器上面已经介绍过。哦额,忘记了,这个类里面还有两个很重要的方法,是重写了其父类Animation的,那就是applyTransformation()方法和·initialize()方法。先说initialize()方法,顾名思义就是初始化的操作,源码如下:
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);
mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);
}
可以看到,在RoateAnimation中该方法首先调用了父类的initialize()方法,其父类中的initialize()中会调用Animation的reset()方法,该方法中只有简单几行动画状态参数的初始化,再无其他。那么现在问题来了,动画是怎么被触发的呢?不激动,这得从我们如何开始一个动画说起,我们启动一个动画不就是一个mView.startAnimation()吗!那我们就去这个方法里找找去,startAnimation()源码如下:
/**
* Start the specified animation now.
*
* @param animation the animation to start now
*/
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
//给该View设置一个动画
setAnimation(animation);
//使父容器缓存失效
invalidateParentCaches();
//意思就是使这个View失效,那么它就需要进行重绘,上面设置了一个setAnimation(),那么重绘时就会执行相应的动画了
//关于动画的一切都从这里开始,下一篇博客会从这个方法开始讲起
invalidate(true);
}
在RoateAnimation中还有一个方法applyTransformation(),这个方法是很关键的,名字直译过来就是应用变换,也就是开始动画的变换,动画的真正实现也是从这里开始,下面我们看下其源码:
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
float scale = getScaleFactor();
if (mPivotX == 0.0f && mPivotY == 0.0f) {
t.getMatrix().setRotate(degrees);
} else {
//如果有旋转点就设置旋转的中心点,先获取一个Matrix(矩阵)
t.getMatrix().setRotate(degrees, mPivotX * scale, mPivotY * scale);
}
}
首先说下Transformation是个什么类,看其解释:
/**
* Defines the transformation to be applied at
* one point in time of an Animation.
*意思就是:定义一个动画执行时的某个时间点的变换,这个变换是一个名词哈!稍后深入原理我们会讲到这个类。
*/
再看下Matrix的setRotate里面是个什么东东:
/**
* Set the matrix to rotate about (0,0) by the specified number of degrees.
* 这里调用了native_setRotate()方法,这个方法并不是java实现的,而是C,这个方法声明在Matrix如:
* private static native void native_setRotate(int native_object,float degrees, float px, float py);
*/
public void setRotate(float degrees) {
native_setRotate(native_instance, degrees);
}
可以看到,动画的实现实际上是调用底层方法实现的,java本身并不实现。这里不对Matrix做过多介绍,我们只是初窥,下一篇博客会有详细的介绍。补间动画就到此为止了,本文以RoateAnimation为例,将了补间动画在xml中和java代码中的使用。下面接着将属性动画。
二、属性动画(Property Animation)
属性动画是3.0后才有的特性,它比比补间动画要灵活且实现的功能更多一些,首先看·下关于属性动画的几个类:
(1)ValueAnimator: 动画执行类
(2)ObjectAnimator:动画执行类,是ValueAnimator的子类
(3)AnimatorSet:复杂组合动画,可以同时完成多个属性动画
(4) AnimatorInflater:用于加载xml的属性文件动画,和AnimationUtils.loadAnimation()作用一样
(5) TypeEvaluator : 类型估值,主要用于设置动画操作属性的值
(6)TimeInterpolator:时间插值,也就是插值器
1、ObjectAnimator的用法
(1)从xml加载属性动画
xml的旋转属性动画文件如下:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:propertyName="rotationX"
android:repeatCount="120"
android:valueFrom="0.0"
android:valueTo="360.0"
android:valueType="floatType" />
上面的动画是让一个View绕X轴完成360°旋转,每次动画执行3秒,重复120次,值得注意的是propertyName属性,该属性是一个View中所必须有的属性,并且为该属性提供了getter和setter方法,在xml文件中,该属性是一个enum类型,用alt+/可以查看 - -、如果在代码中不确定VIew有哪些属性,也可以用这种方式查看啊。。。在xml中布局完毕,就需要在代码中加载了,实现如下:
ObjectAnimator objAnima = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.property_anima);
objAnima.setTarget(mImageView);
objAnima.start();
很简单吧,一定要注意,在xml中不能为动画设置View目标,必须在代码中设置。下面看下在布局文件中使用set实现多动画组合,文件如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
>
<!-- 绕X轴旋转 -->
<objectAnimator
android:duration="30000"
android:propertyName="rotationX"
android:repeatCount="120"
android:valueFrom="0.0"
android:valueTo="360.0"
android:valueType="floatType" />
<!-- 绕Y轴旋转 -->
<objectAnimator
android:duration="30000"
android:propertyName="rotationY"
android:repeatCount="120"
android:valueFrom="0.0"
android:valueTo="360.0"
android:valueType="floatType" />
</set>
这个动画完成的就是同时完成X轴和Y轴旋转,3D效果,在set节点中有一个ordering属性,该属性有两个可选值:sequentially表示动画一个个执行,together表示同时执行。在代码中加载也是一样的,只是把上面的ObjectAnimator改成AnimatorSet即可。
(2)在代码中直接创建ObjectAnimator动画
代码如下,实现和上面的一样绕着X轴旋转的效果
ObjectAnimator objAnima = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0F,360F);
objAnima.setDuration(3000);
objAnima.start();
是一个代码使用需要注意的是,第二个参数,这个参数必须是View的一个具有getter和setter方法的参数,后面两个参数表示起始和结束角度(如果是平移则是距离了),可以是只有一个参数,默认一个起始位置参数。现在需求又变了,让VIew绕X轴、Y轴同时旋转并且期间有一个透明度变换,代码实现如下:
ObjectAnimator objAnima = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0F,360F);
objAnima.setDuration(3000);
ObjectAnimator objAnima1 = ObjectAnimator.ofFloat(mImageView, "rotationY", 0.0F,360F);
objAnima1.setDuration(3000);
ObjectAnimator objAnima2 = ObjectAnimator.ofFloat(mImageView, "alpha",1.0F,0.2F);
objAnima2.setDuration(3000);
AnimatorSet animaSet = new AnimatorSet();
//设置objAnima和objAnima1、objAnima2同时执行
animaSet.play(objAnima).with(objAnima1);
animaSet.play(objAnima1).with(objAnima2);
animaSet.start();
2、ValueAnimator用法
ValueaNnimator并不需要指定属性名称,而是通过监听来手动设置属性的,使用如下:
ValueAnimator valueAnima = ValueAnimator.ofFloat(0.0F,360F);
valueAnima.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator va) {
float value = (Float) va.getAnimatedValue();
mImageView.setRotationX(value);
mImageView.setRotationY(value);
}
});
valueAnima.setDuration(3000);
valueAnima.start();
现在我们知道了属性动画的基本用法,用法很是灵活,可以根据需求来实现很多丰富的效果,在Animator中还有两个重要的方法,那就是setEvaluator()和setInterpolator(null);关于这两个方法我们在动画下一篇博客里会说到。
三、动画的高级用法
1、补间动画的差之器Interpolator
插值器是一个时间插值类,实际上是一个速率的控制类,就是控制了动画在执行的过程中速率的控制,Interpolator是一个空的接口,源码如下:
/**
* An interpolator defines the rate of change of an animation. This allows
* the basic animation effects (alpha, scale, translate, rotate) to be
* accelerated, decelerated, repeated, etc.
*/
public interface Interpolator extends TimeInterpolator {
// A new interface, TimeInterpolator, was introduced for the new android.animation
// package. This older Interpolator interface extends TimeInterpolator so that users of
// the new Animator-based animations can use either the old Interpolator implementations or
// new classes that implement TimeInterpolator directly.
}
可以看到,里面没有任何方法,但是实现该接口的有很多类,这里我直接引用别人的博客的差之器:- AccelerateDecelerateInterpolator============动画开始与结束的地方速率改变比较慢,在中间的时候加速。
- AccelerateInterpolator===================动画开始的地方速率改变比较慢,然后开始加速。
- AnticipateInterpolator ==================开始的时候向后然后向前甩。
- AnticipateOvershootInterpolator=============开始的时候向后然后向前甩一定值后返回最后的值。
- BounceInterpolator=====================动画结束的时候弹起。
- CycleInterpolator======================动画循环播放特定的次数,速率改变沿着正弦曲线。
- DecelerateInterpolator===================在动画开始的地方快然后慢。
- LinearInterpolator======================以常量速率改变。
- OvershootInterpolator====================向前甩一定值后再回到原来位置。
- PathInterpolator========================新增的,就是可以定义路径坐标,然后可以按照路径坐标来跑动;注意其坐标并不是 XY,而是单方向,也就是我可以从0~1,然后弹回0.5 然后又弹到0.7 有到0.3,直到最后时间
/**
* An interpolator where the rate of change is constant
*
*/
public class LinearInterpolator implements Interpolator {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
//这个参数就是输入的
public float getInterpolation(float input) {
return input;
}
}
以上就是他的全部源码了,啥也没有,是不是很简单,再来一个稍微复杂一点的CycleInterpolator,其源码如下:
/**
* Repeats the animation for a specified number of cycles. The
* rate of change follows a sinusoidal pattern.
*
*/
public class CycleInterpolator implements Interpolator {
public CycleInterpolator(float cycles) {
mCycles = cycles;
}
public CycleInterpolator(Context context, AttributeSet attrs) {
TypedArray a =
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.CycleInterpolator);
mCycles = a.getFloat(com.android.internal.R.styleable.CycleInterpolator_cycles, 1.0f);
a.recycle();
}
//这个值input就是一个小数,在0~1之间,0表示动画的开始,1表示动画结束,
public float getInterpolation(float input) {
return (float)(Math.sin(2 * mCycles * Math.PI * input));
}
private float mCycles;
}
代码如此简单,我们自定义一个插值器当然也很简单了,如下是我定义的一个插值器
/**
* 自定义差之器
* @author Administrator
*
*/
private class MyInterpolator implements Interpolator{
@Override
public float getInterpolation(float input) {
//这是我随便写的一个“算法”....
float range = (float) (Math.cos(input)*Math.PI);
return range;
}
}
本文完结。