android动画总共分为三种逐帧动画、补间动画、属性动画。
逐帧动画:主要就是将几张图片放在一起播放形成动画。
补间动画:补间动画还是比较局限的,能实现view的旋转、横竖拉伸、横竖平移、透明度等简单的变化。
由于android速度发展之快,原有的两种动画已经不能满足我们的需求,所以android在3.0版本推出了一个高大上的动画效果,属性动画。
TypeEvaluator的动画效果。那么下面我们进入正题。
三、objectAnimator和valueAnimator的简单用法
1、其实这两个动画先介绍哪两个都无所谓,但由于objectAnimatorValueAnimator的子类,那我们就先编写一个ValueAnimator的例子,之后再去扩展ObjectAnimator下面看代码:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f,360.0f);
valueAnimator.setDuration(1000);
valueAnimator.setTarget(imSimpleValueanimactionIcon); //这个地方是一定要设置的 不然不知道是哪个对象的 设置是哪个对象使用此动画
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
imSimpleValueanimactionIcon.setRotationY((Float)animation.getAnimatedValue());
}
});
此段代码主要就是实现了一个功能,那就是imSimpleValueanimactionIcon这个View的旋转,首先我们去创建一个ValueAnimator的对象,创建ValueAnimator的方法有很多ofFloat是设置动画执行的开始和结束的float类型的值,0.0f为开始360f为结束,还有很多方法,ofInt、ofObject等。用法都是大同小异。在穿件对象之后,我们又去通过setDuration设置了动画执行的时间,ms是时间单位。然后我们去设置了这个动画要在哪个对象上面执行,调用了setTarget方法。最后设置了valueAnimator的开始,不过ValueAnimator并不知道我们设置的值需要执行在view的哪个属性上面,所以我们 需要监听动画的执行过程,通过获取执行过程中的值去设置view属性,这样我们通过addUpdateListener来获取动画执行到当前的值,animation.getAnimatedValue()此方法可以获取动画执行到当下的值是多少,一定是一个我们设置的值的中间数。然后我们通过设置旋转Y的角度去设置一下view的属性就可以实现动画效果了。
看完此段代码我们会想到去监听动画的值有些麻烦,也就是因为有这样麻烦的思想我们也就进入了ObjectAnimator,
ObjectAnimator是不需要监听动画执行过程中的值的,因为系统会自动为我们设置下面看代码:
ObjectAnimator animator = ObjectAnimator.ofFloat(view,"rotation",0.0f,360f); //这个rotation是系统内置的
animator.setDuration(1000);
animator.start();
是不是简单了很多,这也是ObjectAnimator的一个优势,编写简单,不过理解相对繁琐一点,下面解释代码,首先我们还是通过老方法创建ObjectAnimator对象,创建对象的方法和ValueAnimator的方法几本相同,不过参数有些不同,我解释一下参数,第一个参数的意思是动画执行的对象,第二个参数是动画要操作的属性,这个属性系统定义了一些
基本上可以实现和补间动画相同的效果,不过我们也可以在view中自己定义一个属性,不过此属性一定要有getter和setter方法,如果没有这两个方法动画会实现不了,我们可以自定义一个color来改变颜色,在最后我们讲一个自定义属性的ObjectAnimator动画。执行程序我们会看到view旋转的效果。
animName.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) { //动画取消执行调用
}
});
AnimatorSet animSet = new AnimatorSet();
animSet.play(valueAnimator1).with(objectAnimator1); //一起播放
animSet.play(valueAnimator2).after(objectAnimator2); //之后播放
animSet.play(valueAnimator3).before(objectAnimator3); //之前播放
animSet.setDuration(5000);
animSet.start();
PropertyValuesHolder X = PropertyValuesHolder.ofFloat("alpha", 1f,
0f, 1f);
PropertyValuesHolder Y = PropertyValuesHolder.ofFloat("scaleX", 1f,
0, 1f);
PropertyValuesHolder Z = PropertyValuesHolder.ofFloat("scaleY", 1f,
0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, X, Y,Z).setDuration(1000).start();
ballView.animate().alpha(0).setDuration(5000).setInterpolator(new AndyaInterpolator());
//仅此一行代码就可以了
public interface TimeInterpolator { /** * Maps a value representing the elapsed fraction of an animation to a value that represents * the interpolated fraction. This interpolated value is then multiplied by the change in * value of an animation to derive the animated value at the current elapsed animation time. * * @param input A value between 0 and 1.0 indicating our current point * in the animation where 0 represents the start and 1.0 represents * the end * @return The interpolation value. This value can be more than 1.0 for * interpolators which overshoot their targets, or less than 0 for * interpolators that undershoot their targets. */ float getInterpolation(float input);
package com.transfar.andya.propertyanimactiondemo.TimeInterpolatorClass;
import android.animation.TimeInterpolator;
/**
* Created by andYa on 2017/1/7.
*/
public class AndYaTimeInterpolator implements TimeInterpolator{
@Override
public float getInterpolation(float input) {
return (float) (Math.tan((input * 2 - 1) / 4 * Math.PI)) / 2.0f + 0.5f;
}
}
public interface TypeEvaluator<T> { /** * This function returns the result of linearly interpolating the start and end values, with *fraction representing the proportion between the start and end values. The * calculation is a simple parametric calculation:
result = x0 + t * (x1 - x0), * where
x0 is
startValue,
x1 is
endValue, * and
t is
fraction. * * @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 *
fraction parameter. */ public T evaluate(float fraction, T startValue, T endValue); }
/**
* Created by andYa on 2017/1/6.
*/
public class Point {
private float Y;
private float X;
public Point(float X,float Y){
this.X = X;
this.Y = Y;
}
public float getY() {
return Y;
}
public float getX() {
return X;
}
public void setY(float y) {
Y = y;
}
public void setX(float x) {
X = x;
}
}
entry我们定义完成了,那么我们就去自定义一下view,这个view我们只要定义一个画笔,然后去ondraw就可以了
/**
* Created by andYa on 2017/1/6.
*/
public class BallView extends View {
public static final float RADIUBALL = 30f;
public Point currentPoint;
private Paint mPaint;
public BallView(Context context) {
super(context);
}
public BallView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLACK);
}
public BallView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void onDraw(Canvas canvas){
if(currentPoint == null){
currentPoint = new Point(RADIUBALL, RADIUBALL);
drawCricle(canvas);
}else{
drawCricle(canvas);
}
}
private void drawCricle(Canvas canvas){
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x,y,RADIUS,mPaint);
}
}
此段代码我们首先初始化了一个画笔,然后设置为黑色,然后我们又设置了半径currentPoint这个点就是给外界暴露的一个点,如果这个点为空,那我们就先给这个点赋上初始值,如果不为空我们就会ondraw()这个点,在动画改变点的时候我们一定要去调用invalidate()这个方法不然不会重绘的。下面我们就看一下最关键的地方TypeEvaluator:
public class PorpertyTypeEvaluator implements TypeEvaluator{
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint = (Point)startValue;
Point endPoint = (Point)endValue;
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
Point point = new Point(x, y);
return point;
}
}
//调用的局部代码:
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PorpertyTypeEvaluator(),new Point(0f,0f),new Point(300f,300f));
valueAnimator.setTarget(ballView);
valueAnimator.setInterpolator(new AndYaTimeInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
ballView.currentPoint = (Point) animation.getAnimatedValue();
ballView.invalidate(); //这个地方一定要让小球重绘不然是没效果的
}
});
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
mPaint.setColor(Color.parseColor(color));
invalidate();
}
public class ColorTypeEvaluator implements TypeEvaluator{ //这个typeevaluator就是返回当前的object所以此color就是返回fraction对应的颜色的值
private int mCurrentRed = -1;
private int mCurrentGreen = -1;
private int mCurrentBlue = -1;
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
String startColor = (String) startValue;
String endColor = (String) endValue;
int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);
int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);
// 初始化颜色的值
if (mCurrentRed == -1) {
mCurrentRed = startRed;
}
if (mCurrentGreen == -1) {
mCurrentGreen = startGreen;
}
if (mCurrentBlue == -1) {
mCurrentBlue = startBlue;
}
// 计算初始颜色和结束颜色之间的差值
int redDiff = Math.abs(startRed - endRed);
int greenDiff = Math.abs(startGreen - endGreen);
int blueDiff = Math.abs(startBlue - endBlue);
int colorDiff = redDiff + greenDiff + blueDiff;
if (mCurrentRed != endRed) {
mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,
fraction);
} else if (mCurrentGreen != endGreen) {
mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,
redDiff, fraction);
} else if (mCurrentBlue != endBlue) {
mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,
redDiff + greenDiff, fraction);
}
// 将计算出的当前颜色的值组装返回
String currentColor = "#" + getHexString(mCurrentRed)
+ getHexString(mCurrentGreen) + getHexString(mCurrentBlue);
return currentColor;
}
/**
* 根据fraction值来计算当前的颜色。
*/
private int getCurrentColor(int startColor, int endColor, int colorDiff,
int offset, float fraction) {
int currentColor;
if (startColor > endColor) {
currentColor = (int) (startColor - (fraction * colorDiff - offset));
if (currentColor < endColor) {
currentColor = endColor;
}
} else {
currentColor = (int) (startColor + (fraction * colorDiff - offset));
if (currentColor > endColor) {
currentColor = endColor;
}
}
return currentColor;
}
/**
* 将10进制颜色值转换成16进制。
*/
private String getHexString(int value) {
String hexString = Integer.toHexString(value);
if (hexString.length() == 1) {
hexString = "0" + hexString;
}
return hexString;
}
}
ObjectAnimator objectAnimator = ObjectAnimator.ofObject(ballView,"color",new ColorTypeEvaluator(),"#0000FF", "#FF0000");
objectAnimator.setTarget(ballView);
objectAnimator.setDuration(5000);
objectAnimator.start();
调用也是非常方便的如果我们在加上