前言:成功只屬於永不放棄的人。凡諸事有成者都有驚人的毅力做後盾,相信自己,持之以恆。
上一篇文章給大家介紹了ValueAnimator的基本用法,都是比較簡單的用法,這裏帶大家學習一下有關加速器(Interpolator)、計算器(Evaluator)、ValueAnimator的ofObject用法等相關知識。
一、插值器(Interpolator)
插值器(也叫加速器)有關的效果我在《Android動畫篇(二)—— 代碼生成alpha、scale、translate、rotate、set及插值器動畫》做了演示。
什麼是插值器?我們知道通過ofInt(0,400)定義了動畫區間值爲0-400,然後通過添加AnimatorUpdateListener來監聽動畫實時變化,那麼這個變化是怎麼樣變的呢?是勻速變化還是有快有慢,如果我想先減速後加速,那麼該怎麼做呢?這就是插值器的作用,插值器就是用來控制動畫區間值如果計算出來的。比如LinearInterpolator是勻速計算返回區間點的值,而DecelerateInterpolator則是開始變快,後期變慢,其他都類似。
1、插值器的使用
我們使用彈跳插值器(BounceInterpolator)爲例做一個簡單的例子,BounceInterpolator就是在動畫結束的時候彈跳起來。
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value +
mTextView.getHeight());
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
效果圖如下:
如果大家看懂了上一篇文章,這裏的代碼就非常容易理解了。在監聽中,我只改變了top和bottom的位置,跟着動畫變化的值來改變top和bottom的值,setDuration(1000)設置了動畫時長,setInterpolator()設置了插值器,也就是過渡值的變化規則,插值器的意義其實就是相當於物理學中的加速度參數,這也是像加速器的原因。
2、自定義插值器
(1)概述
我們先看看最簡單的勻速插值器LinearInterpolator是怎麼樣的
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return input;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
}
abstract public class BaseInterpolator implements Interpolator {
private @Config int mChangingConfiguration;
/**
* @hide
*/
public @Config int getChangingConfiguration() {
return mChangingConfiguration;
}
/**
* @hide
*/
void setChangingConfiguration(@Config int changingConfiguration) {
mChangingConfiguration = changingConfiguration;
}
}
在上面的可以得知LinearInterpolator繼承了BaseInterpolator實現了Interpolator接口,而Interpolator 接口直接繼承自TimeInterpolator,而且Interpolator 沒有添加其他的方法,我們來看看TimeInterpolator這個接口:
/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
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);
}
在TimeInterpolator裏面只有一個函數, float getInterpolation(float input):
- float input 是float類型,範圍是0-1,表示當前動畫的進度,0表示動畫剛剛開始,1表示動畫結束。0.5表示動畫中間位的位 置,其他的以此類推
- 返回值 表示當前實際想要顯示的進度,取值可以大於1也可以小於0,大於1表示超出動畫位置,小於0表示小於開始位置
input參數表示當前動畫的進度,勻速增加的。動畫進度就是動畫時間上的進度,與我們任何設置無關,只與時間有關,隨時間的增加,動畫進度自然增加,從0到1,我們在setDuration(1000)設置了動畫時長的,動畫在該時間內完成。
而返回值表示動畫進度的具體數值,它的取值範圍是對應ofInt()、ofFloat()來指定的,這個返回值就表示當前時間動畫的具體進度值。
ValueAnimator valueAnimator = ValueAnimator.ofInt(100, 400);
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value + mTextView.getHeight());
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
從上面的代碼知道,我們設置了AnimatorUpdateListener後,在監聽回調函數中設置animation.getAnimatedValue()就可以獲得動畫具體進度值,那麼這個值是怎麼來的呢?
計算公式:當前進度值 = 100+(400-100)*顯示進度
其中100和400就是我們ofInt(100,400)設置的起點值和終點值,我們要計算位置是,在起始值100的基礎上加上實際運動距離(400-100)乘以運動進度。ofInt中的animation.getAnimatedValue()的值就是這麼來的。
我們知道了input與getInterpolation()返回值的關係了,來看看LinearInterpolator 是如何重寫getInterpolation()方法的?
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
········
public float getInterpolation(float input) {
return input;
}
········
}
LinearInterpolator 在getInterpolation()函數中,直接把input返回了,把動畫的進度作爲動畫具體的數值進度,也就是動畫進度和時間進度一致,比如動畫進度爲0,那麼動畫具體數值進度也爲0,動畫進度爲0.5,那麼動畫具體數值進度也爲0.5。
(2)實例
自定義插值器,繼承BaseInterpolator就可以了,實現其中的方法getInterpolation(float input),我們將該方法的返回值作了修改,我們將進度翻轉過來。然後使用我們的插值器。
public class MyInterpolator extends BaseInterpolator {
@Override
public float getInterpolation(float input) {
return 1 - input;
}
}
使用自定義的插值器
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
valueAnimator.setDuration(1000);
//設置自定義的插值器
valueAnimator.setInterpolator(new MyInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value +
mTextView.getHeight());
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
效果如下:
我們在將進度數值倒敘返回後,TextView在結束爲止(400)返回到開始位置(0)。
到這裏,想必大家已經瞭解getInterpolation(float input)函數中input參數與返回值的關係,在重寫插值器時,需要強有力的數學知識做支持,一般都是通過數學公式來計算插值器的變化趨勢。你們可以在分析幾個其他插值器的寫法,可以總結成公式,放在公式插圖軟件裏面,看看對應的數學圖在(0,1)之間的走向,這個走勢就是插值器在數值變化時的樣子。
二、計算器(Evaluator)
1、概述
上面我們提到通過監聽器拿到當前動畫所對應的具體數值,而不是百分制的進度,那麼就有一個地方,根據當前的進度值轉化爲對應的數值,這個地方就是計算器(Evaluator),Evaluator就是將加速器返回的進度值轉化爲相對於的數值。其實Evaluator就是一個轉換器,他能把小數進度轉換成對應的數字值。
上面提到的公式:
當前值 = 100 + (400-100)*顯示進度
這個公式就是在Evaluator內計算的,在拿到當前數字進度所對應的值後將其返回。看看下面這幅圖:
上面這幅圖是從定義動畫的數值區間到通過AnimatorUpdateListener獲得當前動畫所對應具體數值的整個過程,解釋一下:
(1)ofInt(0,400)表示指定動畫的數值區間,是從0運動到400;
(2)在動畫開始之後,通過加速器返回動畫進度所對應的數字進度,這個數字進度是百分制的,以小數表示,比如0.2;
(3)Evaluator將加速器返回的進度值轉化爲相對於的數值;
(4)通過AnimatorUpdateListener監聽器使用animation.getAnimatedValue()函數獲得Evaluator中返回的數值。
2、各種Evaluator
無論是什麼動畫它的進度必然是在0-1之間,0表示沒開始,1表示結束,任何動畫都適用。但是Evaluator不一樣,我們知道Evaluator是用來根據加速器Interpolator返回的小數進度轉換成當前的數值進度。
那麼使用ofInt()來定義動畫,動畫中的值都是int類型,所對應的Evaluator返回值時,也是返回int類型的數值;如果使用ofFloat()來定義動畫,動畫中的值都是float類型,所對用的Evaluator返回值時,也是返回float類型的數值。所以每種Evaluator的定義方式必定有對應的Evaluator來使用,他的專用原因在於數值動畫類型不一致,在通過Evaluator返回時會報類型強轉錯誤。所以在動畫數值類型一致時,Evaluator才能通用,ofInt()對應IntEvaluator,ofFloat()對應FloatEvaluator,在設置Evaluator時通setEvaluator(TypeEvaluator value)來設置:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
valueAnimator.setDuration(1000);
valueAnimator.setEvaluator(new IntEvaluator());
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value +
mTextView.getHeight());
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
但是之前爲什麼沒有設置Evaluator的時候也能正常使用呢?因爲ofInt()和ofFloat()是系統直接提供的函數,在使用時都有默認的Interpolator和Evaluator,沒有設置則使用默認的,我們看看IntEvaluator的內部實現:
/**
* This evaluator can be used to perform type interpolation between <code>int</code> values.
*/
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));
}
}
可以看到IntEvaluator裏面只有一個函數evaluate(float fraction, Integer startValue, Integer endValue),其中fraction就是加速器中的返回值,表示當前動畫的數值進度,百分制小數表示。startValue和endValue分別對應ofInt(int start , int end)的起始值和結束值。比如上面的動畫ofInt(100,400)進行到40%時,在Evaluator函數中fraction=0.4,startValue = 100,endValue = 400;
下面我們看看進度小數值計算出來的具體數值:
return (int)(startInt + fraction * (endValue - startInt));
這不正是對應着我上面提到的公式嗎?
當前值 = 100 + (400-100)*顯示進度
計算原理我們在上面已經講過,根據進度來計算具體數值。我們來自定義一個Evaluator
3、自定義Evaluator
(1)簡單實現MyEvaluator
我們定義一個Evaluator,首先實現TypeEvaluator<>,根據數值類型傳入泛型類型:
public class MyEvaluator implements TypeEvaluator<Integer> {
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
return null;
}
}
我們來實現簡單的evaluate函數:
return (int) (200 + startValue + fraction * (endValue - startValue));
根據IntEvaluator的基礎上做了修改,讓返回值增加了200,所以開始定義的區間是ofInt(0,400)但是實際返回的區間是ofInt(200,600),看看怎麼使用:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 400);
valueAnimator.setDuration(1000);
valueAnimator.setEvaluator(new MyEvaluator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mTextView.layout(mTextView.getLeft(), value, mTextView.getRight(), value +
mTextView.getHeight());
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
效果圖如下:
從效果圖可看出TextView的動畫位置都向下移動了200。
在加速器Interpolator中,我們通過自定義加速器返回的數值進度來返回數值的位置,比如上面我們實現的倒敘動畫
在計算器Evaluator中,通過改變進度值所對應的具體數值來改變數值位置。
結論:我們可以通過重寫加速器Interpolator來改變數值進度來改變數值位置,也可以通多改變計算器Evaluator中進度所對應的數值來改變數值位置。
(2)實現倒敘輸出
我們來定義一個ReverseEvaluator來實現倒敘:
public class ReverseEvaluator implements TypeEvaluator<Integer> {
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
return (int) (endValue - fraction * (endValue - startValue));
}
}
其中fraction * (endValue - startValue)表示實際運動的距離,endValue - fraction * (endValue - startValue)表示離終點的距離有多少,實現了從終點出發,最終到達起點。我們使用試一試:
valueAnimator.setEvaluator(new ReverseEvaluator());
效果如下:
4、關於ArgbEvaluator
ArgbEvaluator使用來做顏色值轉換的,我們先來使用一下,最後再講源碼:
在源碼我們可以看出返回值時Int類型,可以使用ofInt()來初始化動畫的取值範圍,顏色值包括A、R、G、B四個值,每個值都是8位,用16進制來表示顏色值。
ValueAnimator valueAnimator = ValueAnimator.ofInt(0xff000000, 0xffffffff);
valueAnimator.setDuration(2000);
valueAnimator.setEvaluator(new ArgbEvaluator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mTextView.setBackgroundColor(value);
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
我們將動畫的取值範圍定義爲ofInt(0xff000000, 0xffffffff),即從黑色變爲白色,在AnimatorUpdateListener中將回傳回來的顏色值設置在TextView的背景顏色中。
點擊進去看一看ArgbEvaluator的源碼:
/**
* This function returns the calculated in-between value for a color
* given integers that represent the start and end values in the four
* bytes of the 32-bit int. Each channel is separately linearly interpolated
* and the resulting calculated values are recombined into the return value.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @param endValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @return A value that is calculated to be the linearly interpolated
* result, derived by separating the start and end values into separate
* color channels and interpolating each one separately, recombining the
* resulting values in the same way.
*/
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
這段代碼主要分爲三部分:
(1)根據startValue求出A、R、G、B中各個色彩的初始值;
(2)根據endValue求出A、R、G、B的各個結束值;
(3)根據當前動畫百分比進度求出對應的值。
第一部分:根據startValue求出A、R、G、B中各個色彩的初始值
int startInt = (Integer) startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
這段代碼是根據位移和與運算計算出顏色值中A、R、G、B各部分對應的值,顏色值與A、R、G、B值關係如下:
Android中的顏色值遵循RGB/ARGB標準,使用時通常以"#"爲爲開頭的8位16進製表示。其中ARGB依次代表透明度(alpha)、紅色(red)、綠色(green)、藍色(blue);取值範圍爲0~255,即十六進制的0x00~0xff表示。A從0x00到0xff表示完全透明到完全不透明,RGB從0x00到0xff表示顏色從淺到深。
所以我們的初始值是0xff000000,求出來的startA = 0xff,startR = 0x00,startG = 0x00,startB = 0x00;關於位移和與運算我就不講了。
第二部分:和第一部分原理一致,根據endValue求出A、R、G、B的各個結束值的顏色值:
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
我們的結束值是:0xffffffff,求出來的endA = oxff,endR = oxff,endG = oxff,endB = oxff;
最後一部分:根據當前動畫百分比進度求出對應的值
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
對於這個計算都很容易理解,其實和Evalustor的計算公式一樣,根據起始值和結束值計算出該進度下的具體值。最後通過位移和與運算將當下的ARGB各個值組合成當前的顏色值。
三、ofObject
1、ofObject的概述
前面講了ValueAnimator使用ofInt()和ofFloat()來定義動畫,但是ofInt()和ofFloat()分別只能傳入Int和Float類型的參數,如果我們需要其他操作類型的怎麼辦?ValueAnimator還有一個ofObject()類型的參數,可以傳入任何類型的變量:
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
它有兩個參數,一個是自定義的Evaluator;一個是可變長參數,object類型
大家可能有疑問,爲什麼要自定義Evaluator,因爲Evaluator是根據動畫當前的進度來計算當前對應的值的,如果可變參數類型是object,那麼具體的類型就是未知的,所以Evaluator無法根據類型來得出結果,進度值的轉換過程也必須由我們來做,我們來看看ofObject()的使用效果:
從效果圖可以看出,字母A變化到Z,並且變化速度越來越快。
ValueAnimator valueAnimator = ValueAnimator.ofObject(new CharEvaluator(), new Character('A'), new Character('Z'));
valueAnimator.setDuration(6000);
valueAnimator.setInterpolator(new AccelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
char value = (char) animation.getAnimatedValue();
mTextView.setText(String.valueOf(value));
Log.e(TAG, "value:" + value);
}
});
valueAnimator.start();
這裏注意三點:
(1)構造器:
ValueAnimator.ofObject(new CharEvaluator(), new Character('A'), new Character('Z'));
我們定義了一個CharEvaluator(後面會講),初始化動畫時,傳入Character對象,起始是A,結束是Z,利用動畫自動變化字母A到字母Z,具體的實現就是CharEvaluator的事情了;
(2)AnimatorUpdateListener監聽
char value = (char) animation.getAnimatedValue();
mTextView.setText(String.valueOf(value));
通過animation.getAnimatedValue()得到當前動畫字符,在動畫過程中通過Evaluator返回值的類型必然和構造時的類型時一樣的,由於傳入的是Character,所以得到的也是Character,轉爲字符串後設置到TextView;
(3)插值器
valueAnimator.setInterpolator(new AccelerateInterpolator());
這裏我們使用的加速插值器,隨着動畫的進行,速度會越來越快。
好,最關鍵的地方到了,CharEvaluator是怎麼實現的呢?ASCII碼錶中的數值與字符轉換的方法,每一個字符都有一個跟它相對應的數值,字母A到字母Z所對應的數值區間爲65-90,在程序中我們可以通過強轉來改變類型。
比如:數字轉字符
char temp = (char)65;//得到的temp的值是大寫字母A
字符轉數值
char temp = 'A';
int num = (int)temp;//這裏的到的數值就是ASCII碼錶中A對應的值65
CharEvaluator的實現:
public class CharEvaluator implements TypeEvaluator<Character> {
@Override
public Character evaluate(float fraction, Character startValue, Character endValue) {
int startInt = (int) startValue;
int endInt = (int) endValue;
int value = (int) (startInt + fraction * (endInt - startInt));
return (char) value;
}
}
在這裏,我們就是利用A-Z字母在ASCII字碼表中數值遞增的原理,先求出對應字符的數值,再將數值轉化爲具體字符。
2、ofObject的自定義對象
上面我們初度使用了ofObject(),ofObject()能初始化任何對像,我們來自定義一個對象,然後利用ofObject()來構造這個對象的動畫。
在這裏,我們自定義一個view,在view上畫一個圓,有動畫效果,插值器使用的是回彈插值器(BounceInterpolator)
(1)定義類Point
public class Point {
private int radius;
public Point(int radius) {
this.radius = radius;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
}
point類很簡單,只有一個成員變量radius
(2)自定義MyPointView
public class MyPointView extends View {
private Point mPoint;
public MyPointView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mPoint != null) {
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
canvas.drawCircle(500f, 600f, mPoint.getRadius(), paint);
}
}
public void doAnimator() {
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.start();
}
}
我們先來理一理這裏的代碼:先看看外部調用的動畫函數
public void doAnimator() {
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300));
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new BounceInterpolator());
valueAnimator.start();
}
ofObject()的構造方法:
ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(20), new Point(300));
這裏可以看到,在構造動畫時,動畫所對應的值是Point類型,那麼在Evaluator中的返回值類型也是Point,有關PointEvaluator下面再講,下面看看監聽中的代碼:
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
在監聽過程中,通過animation.getAnimatedValue()得到的mPoint實例,保存在成員變量中,然後 invalidate()強制刷新View。在強制刷新後,會走onDraw()方法:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mPoint != null) {
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
canvas.drawCircle(500f, 600f, mPoint.getRadius(), paint);
}
}
這裏設置畫筆的屬性,在(500,600)的位置畫出圓形。在構造ofObject()函數中,初始值和中間值都是Point類型,PointEvaluator的返回值類型也是Point:
public class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startPoint, Point endPoint) {
int startInt = startPoint.getRadius();
int endInt = endPoint.getRadius();
int value = (int) (startInt + fraction * (endInt - startInt));
return new Point(value);
}
}
這裏根據初始半徑和最終半徑求出當前動畫所對應的半徑值,然後構建一個Point對象。
(3)使用MyPointView
在佈局中添加控件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_start_anim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#666666"
android:padding="10dp"
android:text="start anim"/>
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="@color/colorAccent"
android:padding="10dp"
android:text="Hello World!"
android:textColor="#ffffff"/>
<TextView
android:id="@+id/tv_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="#666666"
android:padding="10dp"
android:text="cancel anim"
android:textColor="#ffffff"/>
<com.example.a04_valueanimator_advanced.MyPointView
android:id="@+id/myPointView"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
調用控件的屬性:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = MainActivity.class.getSimpleName();
private TextView mTextView;
private MyPointView myPointView;
private ValueAnimator mValueAnimator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myPointView = findViewById(R.id.myPointView);
mTextView = findViewById(R.id.tv);
findViewById(R.id.tv_start_anim).setOnClickListener(this);
findViewById(R.id.tv_cancel).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_start_anim:
setMyPointView();
break;
case R.id.tv_cancel:
if (mValueAnimator != null) {
//退出動畫
mValueAnimator.cancel();
}
break;
default:
break;
}
}
/**
* ofObject的使用2
*/
private void setMyPointView() {
myPointView.doAnimator();
}
}
點擊start anim按鈕時調用myPointView.doAnimator()方法開始動畫。
至此,本文結束!篇幅比較長。
源碼下載地址:https://github.com/FollowExcellence/AndroidAnimation
請大家尊重原創者版權,轉載請標明出處: https://blog.csdn.net/m0_37796683/article/details/90483462 謝謝!
動畫系列文章:
1、 Android動畫篇(一)—— alpha、scale、translate、rotate、set的xml屬性及用法
- 補間動畫的XML用法以及屬性詳解
2、Android動畫篇(二)—— 代碼實現alpha、scale、translate、rotate、set及插值器動畫
- 代碼動態實現補間動畫以及屬性詳解
3、 Android動畫篇(三)—— 屬性動畫ValueAnimator的使用
- ValueAnimator的基本使用
4、 Android動畫篇(四)—— 屬性動畫ValueAnimator的高級進階
- 插值器(Interpolator)、計算器(Evaluator)、ValueAnimator的ofObject用法等相關知識
5、 Android動畫篇(五)—— 屬性動畫ObjectAnimator基本使用
- ObjectAnomator的基本使用以及屬性詳解
6、 Android動畫篇(六)—— 組合動畫AnimatorSet和PropertyValuesHolder的使用
- AnimatorSet動畫集合和PropertyValuesHolder的使用
以上幾篇動畫文章是一定要掌握的,寫的不好請多多指出!