文章目录
源码地址
1.ValueAnimator
概述
ValueAnimator不提供任何动画效果,它更像一个数值发生器,用来产生有一定规律的数字,从而让调用者控制动画的实现过程.
估值器
- 估值器(TypeEvaluator)决定 值 的具体变化数值
- ValueAnimator有3个比较常用的方法:ValueAnimator.ofInt(int values),ValueAnimator.ofFloat(float values)和ValueAnimator.ofObject(int values);其中ofInt内置了整形估值器IntEvaluator,ofFloat内置了浮点型估值器FloatEvaluator,ofObject没有默认的估值器
FloatEvaluator
public class FloatEvaluator implements TypeEvaluator<Number> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>float</code> or
* <code>Float</code>
* @param endValue The end value; should be of type <code>float</code> or <code>Float</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Float evaluate(float fraction, Number startValue, Number endValue) {
// fraction:表示动画完成度(根据它来计算当前动画的值)
// startValue、endValue:动画的初始值和结束值
float startFloat = startValue.floatValue();
// 返回对象动画过渡的逻辑计算后的值
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
IntEvaluator
public class IntEvaluator implements TypeEvaluator<Integer> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @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));
}
}
ValueAnimator.ofObject使用的小demo
自定义MyPoint类:
public class MyPoint {
// 设置两个变量用于记录坐标的位置
private float x;
private float y;
// 构造方法用于设置坐标
public MyPoint(float x, float y) {
this.x = x;
this.y = y;
}
// get方法用于获取坐标
public float getX() {
return x;
}
public float getY() {
return y;
}
}
自定义估值器PointEvaluator:
public class PointEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
MyPoint startPoint = (MyPoint) startValue;
MyPoint endPoint = (MyPoint) endValue;
// 根据fraction来计算当前动画的x和y的值
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
// 将计算后的坐标封装到一个新的Point对象中并返回
MyPoint point = new MyPoint(x, y);
return point;
}
}
自定义圆圈CriView
public class CriView extends View{
public static final float RADIUS = 70f;// 圆的半径 = 70
public MyPoint currentPoint;// 当前点坐标
private Paint mPaint;// 绘图画笔
public CriView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// 初始化画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 如果当前点坐标为空(即第一次)
if (currentPoint == null) {
// 创建一个点对象(坐标是(70,70))
currentPoint = new MyPoint(RADIUS, RADIUS);
// 在该点画一个圆:圆心 = (70,70),半径 = 70
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}else{
// 如果坐标值不为0,则画圆
// 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果
// 在该点画一个圆:圆心 = (30,30),半径 = 30
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}
}
public void setPonit(MyPoint ponit) {
this.currentPoint = ponit;
}
}
xml文件使用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.test.ck.propertyanimator.MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="变化" />
<com.test.ck.propertyanimator.CriView
android:id="@+id/criView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
activity内的点击事件:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyPoint startPoint = new MyPoint(CriView.RADIUS, CriView.RADIUS);// 初始点为圆心(70,70)
MyPoint endPoint = new MyPoint(200, 250);// 结束点为(200, 250)
ValueAnimator va = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
va.setDuration(3000);
va.start();
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
MyPoint animatedValue = (MyPoint) animation.getAnimatedValue();
criView.setPonit(animatedValue);
//requestLayout方法只会导致当前view的measure和layout,而draw不一定被执行,
// 只有当view的位置发生改变才会执行draw方法,因此如果要使当前view重绘需要调用invalidate
// criView.requestLayout();
criView.invalidate();
}
});
}
});
2.ObjectAnimator类
常用的属性值:
(ObjectAnimator是继承ValueAnimator的)
• translationX和translationY:用来沿着X轴或者Y轴进行平移。
• rotation、rotationX、rotationY:用来围绕View的支点进行旋转。
• PrivotX和PrivotY:控制View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认该支点位置就是View对象的中心点。
• alpha:透明度,默认是1(不透明),0代表完全透明。
• x和y:描述View对象在其容器中的最终位置
一个沿x轴平移的小demo
button.setOnClickListener(new View.OnClickListener() {
/**
* @param v
* ObjectAnimator.ofFloat(Object object, String property, float ....values);
* 参数说明
* object 需要操作的对象
* property 需要操作的对象的属性(这个属性必须要有get和set方法)
* float ....values:动画初始值 & 结束值(不固定长度)
*/
@Override
public void onClick(View v) {
ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
//设置动画时长
oa.setDuration(2000);
//设置动画延迟播放时间
oa.setStartDelay(500);
// 设置动画重复播放次数 = 重放次数+1 动画播放次数 = infinite时,动画无限重复
oa.setRepeatCount(1);
// 设置重复播放动画模式 ValueAnimator.RESTART(默认):正序重放;ValueAnimator.REVERSE:倒序回放
oa.setRepeatMode(ValueAnimator.REVERSE);
oa.start();
}
});
使用ObjectAnimator.ofObject实现ValueAnimator.ofObject的相同效果
只要需要在自定义的CriView中添加currentPoint属性的get和set方法,并且在set方法中调用 invalidate()方法
public class CriView extends View{
public static final float RADIUS = 70f;// 圆的半径 = 70
public MyPoint currentPoint;// 当前点坐标
private Paint mPaint;// 绘图画笔
public CriView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// 初始化画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 如果当前点坐标为空(即第一次)
if (currentPoint == null) {
// 创建一个点对象(坐标是(70,70))
currentPoint = new MyPoint(RADIUS, RADIUS);
// 在该点画一个圆:圆心 = (70,70),半径 = 70
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}else{
// 如果坐标值不为0,则画圆
// 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果
// 在该点画一个圆:圆心 = (30,30),半径 = 30
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}
}
public void setPonit(MyPoint ponit) {
this.currentPoint = ponit;
}
public MyPoint getCurrentPoint() {
return currentPoint;
}
public void setCurrentPoint(MyPoint currentPoint) {
this.currentPoint = currentPoint;
invalidate();
}
}
然后在activity中这样使用,即可实现
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyPoint startPoint = new MyPoint(CriView.RADIUS, CriView.RADIUS);// 初始点为圆心(70,70)
MyPoint endPoint = new MyPoint(200, 250);// 结束点为(200, 250)
ObjectAnimator oa = ObjectAnimator.ofObject(criView, "currentPoint", new PointEvaluator(), startPoint, endPoint);
oa.setDuration(3000);
oa.start();
}
});
3.AnimatorSet
- play(Animator anim) : 播放当前动画
- after(Animator anim): 将现有动画插入到传入的动画之后执行。
- after(long delay): 将现有动画延迟指定毫秒后执行。
- before(Animator anim): 将现有动画插入到传入的动画之前执行
- with(Animator anim): 将现有动画和传入的动画同时执行
- playTogether(): 动画一起执行
- playSequentially():动画依次执行
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(oa1).with(oa2).after(oa3);
animatorSet.setDuration(5000);
animatorSet.start();
}
});
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(oa1,oa2,oa3);
animatorSet.setDuration(5000);
animatorSet.start();
}
});
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(oa1,oa2,oa3);
animatorSet.setDuration(5000);
animatorSet.start();
}
});