文章目錄
-
- 1.ValueAnimator
-
- 概述
- 估值器
- ValueAnimator.ofObject使用的小demo
- 2.ObjectAnimator類
-
- 常用的屬性值:
- 一個沿x軸平移的小demo
- 使用ObjectAnimator.ofObject實作ValueAnimator.ofObject的相同效果
- 3.AnimatorSet
源碼位址
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();
}
});