android動畫四·Interpolator和ViewPropertyAnimator的用法
動畫
Interpolator的用法
使用屬性動畫時,系統預設的Interpolator其實就是一個先加速後減速的Interpolator,對應的實現類就是AccelerateDecelerateInterpolator。
-
9種插值器:(系統預設的)
- AccelerateDecelerateInterpolator / 先加速再減速
- AccelerateInterpolator / 加速
- AnticipateInterpolator / 先蓄力,回退一小步然後加速前進
- AnticipateOvershootInterpolator / 在上一個基礎上超出終點一小步再回到終點
- BounceInterpolator / 最後階段彈球效果
- CycleInterpolator / 週期運動
- DecelerateInterpolator / 減速
- LinearInterpolator / 勻速
- OvershootInterpolator / 快速到達終點並超出一小步最後回到終點
-
瞭解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);
}
介面還是非常簡單的,只有一個getInterpolation()方法。大家有興趣可以通過註釋來對這個介面進行詳解的瞭解,這裡我就簡單解釋一下,getInterpolation()方法中接收一個input引數,這個引數的值會隨著動畫的執行而不斷變化,不過它的變化是非常有規律的,就是根據設定的動畫時長勻速增加,變化範圍是0到1。也就是說當動畫一開始的時候input的值是0,到動畫結束的時候input的值是1,而中間的值則是隨著動畫執行的時長在0到1之間變化的。
說到這個input的值,有不少朋友可能會聯想到fraction值。那麼這裡的input和fraction有什麼關係或者區別呢?答案很簡單,input的值決定了fraction的值。input的值是由系統經過計算後傳入到getInterpolation()方法中的,然後我們可以自己實現getInterpolation()方法中的演算法,根據input的值來計算出一個返回值,而這個返回值就是fraction了。
因此,最簡單的情況就是input值和fraction值是相同的,這種情況由於input值是勻速增加的,因而fraction的值也是勻速增加的,所以動畫的運動情況也是勻速的。系統中內建的LinearInterpolator就是一種勻速運動的Interpolator,那麼我們來看一下它的原始碼是怎麼實現的:
/**
* An interpolator where the rate of change is constant
*/
@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();
}
}
- 我們再來看一個稍微複雜一點的。既然現在大家都知道了系統在預設情況下使用的是AccelerateDecelerateInterpolator,那我們就來看一下它的原始碼吧,如下所示:
/**
* An interpolator where the rate of change starts and ends slowly but
* accelerates through the middle.
*
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}
@SuppressWarnings({"UnusedDeclaration"})
public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
}
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
}
}
- 我們可以將這個演算法的執行情況通過曲線圖的方式繪製出來,結果如下圖所示:
只不過經過了餘弦運算之後,最終的結果不再是勻速增加的了,而是經歷了一個先加速後減速的過程。
- 編寫自定義Interpolator,讓它變成先減速後加速的方式。新建DecelerateAccelerateInterpolator類,讓它實現TimeInterpolator介面,程式碼如下所示:
public class DecelerateAccelerateInterpolator implements TimeInterpolator{
@Override
public float getInterpolation(float input) {
float result;
if (input <= 0.5) {
result = (float) (Math.sin(Math.PI * input)) / 2;
} else {
result = (float) (2 - Math.sin(Math.PI * input)) / 2;
}
return result;
}
}
這段程式碼是使用正弦函式來實現先減速後加速的功能的,因為正弦函式初始弧度的變化值非常大,剛好和餘弦函式是相反的,而隨著弧度的增加,正弦函式的變化值也會逐漸變小,這樣也就實現了減速的效果。當弧度大於π/2之後,整個過程相反了過來,現在正弦函式的弧度變化值非常小,漸漸隨著弧度繼續增加,變化值越來越大,弧度到π時結束,這樣從0過度到π,也就實現了先減速後加速的效果。
- 同樣我們可以將這個演算法的執行情況通過曲線圖的方式繪製出來,結果如下圖所示:
- 那麼現在我們將DecelerateAccelerateInterpolator在程式碼中進行替換,如下所示:
private void startAnimation() {
Point startPoint = new Point(getWidth() / 2, RADIUS);
Point endPoint = new Point(getWidth() / 2, getHeight() - RADIUS);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
anim.setInterpolator(new DecelerateAccelerateInterpolator());
anim.setDuration(3000);
anim.start();
}
ViewPropertyAnimator的用法
在絕大多數情況下,我相信大家主要都還是對View進行動畫操作的。Android開發團隊也是意識到了這一點,沒有為View的動畫操作提供一種更加便捷的用法確實是有點太不人性化了,於是在Android 3.1系統當中補充了ViewPropertyAnimator這個機制。
- 比如我們想要讓一個TextView從常規狀態變成透明狀態,就可以這樣寫:
textview.animate().alpha(0f);
不過textview.animate()這個方法是怎麼回事呢?animate()方法就是在Android 3.1系統上新增的一個方法,這個方法的返回值是一個ViewPropertyAnimator物件,也就是說拿到這個物件之後我們就可以呼叫它的各種方法來實現動畫效果了,這裡我們呼叫了alpha()方法並轉入0,表示將當前的textview變成透明狀態。
- 那麼怎樣去設定動畫的執行時長呢?很簡單,也是通過連綴的方式設定即可,比如我們想要讓動畫執行5秒鐘,就可以這樣寫:
textview.animate().x(500).y(500).setDuration(5000)
.setInterpolator(new BounceInterpolator());
- Interpolator技術我們也可以應用在ViewPropertyAnimator上面,如下所示:
textview.animate().x(500).y(500).setDuration(5000)
.setInterpolator(new BounceInterpolator());
-
關於ViewPropertyAnimator有幾個細節還是值得大家注意一下的:
- 整個ViewPropertyAnimator的功能都是建立在View類新增的animate()方法之上的,這個方法會建立並返回一個ViewPropertyAnimator的例項,之後的呼叫的所有方法,設定的所有屬性都是通過這個例項完成的。
- 大家注意到,在使用ViewPropertyAnimator時,我們自始至終沒有呼叫過start()方法,這是因為新的介面中使用了隱式啟動動畫的功能,只要我們將動畫定義完成之後,動畫就會自動啟動。並且這個機制對於組合動畫也同樣有效,只要我們不斷地連綴新的方法,那麼動畫就不會立刻執行,等到所有在ViewPropertyAnimator上設定的方法都執行完畢後,動畫就會自動啟動。當然如果不想使用這一預設機制的話,我們也可以顯式地呼叫start()方法來啟動動畫。
- ViewPropertyAnimator的所有介面都是使用連綴的語法來設計的,每個方法的返回值都是它自身的例項,因此呼叫完一個方法之後可以直接連綴呼叫它的另一個方法,這樣把所有的功能都串接起來,我們甚至可以僅通過一行程式碼就完成任意複雜度的動畫功能。
參考:[http://blog.csdn.net/guolin_blog/article/details/44171115]