轉自http://blog.csdn.net/ljx19900116/article/details/41806835
導論
本文着重講解Android3.0後推出的屬性動畫框架Property Animation——Animator。
產生原因
3.0之前已有的動畫框架——Animation存在一些侷限性, Animation框架定義了透明度,旋轉,縮放和位移幾種常見的動畫,而且控制的是整個View,實現原理是每次繪製視圖時View所在的ViewGroup中的drawChild函數獲取該View的Animation的Transformation值,然後調用canvas.concat(transformToApply.getMatrix()),通過矩陣運算完成動畫幀,如果動畫沒有完成,繼續調用invalidate()函數,啓動下次繪製來驅動動畫,動畫過程中的幀之間間隙時間是繪製函數所消耗的時間,可能會導致動畫消耗比較多的CPU資源,最重要的是,動畫改變的只是顯示,並不能相應事件。
而在Animator框架中使用最多的是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator進行更精細化控制,只控制一個對象的一個屬性值,多個ObjectAnimator組合到AnimatorSet形成一個動畫。而且ObjectAnimator能夠自動驅動,可以調用setFrameDelay(longframeDelay)設置動畫幀之間的間隙時間,調整幀率,減少動畫過程中頻繁繪製界面,而在不影響動畫效果的前提下減少CPU資源消耗。因此,Anroid推出的強大的屬性動畫框架,基本可以實現所有的動畫效果。
強大的原因
因爲屬性動畫框架操作的是真實的屬性值,直接變化了對象的屬性,因此可以很靈活的實現各種效果,而不侷限於以前的4種動畫效果。
ObjectAnimator
ObjectAnimator是屬性動畫框架中最重要的實行類,創建一個ObjectAnimator只需通過他的靜態工廠類直接返回一個ObjectAnimator對象。傳的參數包括一個對象和對象的屬性名字,但這個屬性必須有get和set函數,內部會通過java反射機制來調用set函數修改對象屬性值。還包括屬性的初始值,最終值,還可以調用setInterpolator設置曲線函數。
ObjectAnimator實例
-
ObjectAnimator
-
.ofFloat(view,
"rotationX",
0.0F,
360.0F)
-
.setDuration(1000)
-
.start();
這個例子很簡單,針對view的屬性rotationX進行持續時間爲1000ms的0到360的角度變換。
PS:可操縱的屬性參數:x/y;scaleX/scaleY;rotationX/ rotationY;transitionX/ transitionY等等。
- ObjectAnimator
- .ofFloat(view, "rotationX", 0.0F, 360.0F)
- .setDuration(1000)
- .start();
PS:X是View最終的位置、translationX爲最終位置與佈局時初始位置的差。所以若就用translationX即爲在原來基礎上移動多少,X爲最終多少。getX()的值爲getLeft()與getTranslationX()的和。
1. translationX和translationY:這兩個屬性作爲一種增量來控制着View對象從它佈局容器的左上角座標開始的位置。
2. rotation、rotationX和rotationY:這三個屬性控制View對象圍繞支點進行2D和3D旋轉。
3. scaleX和scaleY:這兩個屬性控制着View對象圍繞它的支點進行2D縮放。
4. pivotX和pivotY:這兩個屬性控制着View對象的支點位置,圍繞這個支點進行旋轉和縮放變換處理。默認情況下,該支點的位置就是View對象的中心點。
5. x和y:這是兩個簡單實用的屬性,它描述了View對象在它的容器中的最終位置,它是最初的左上角座標和translationX和translationY值的累計和。
6. alpha:它表示View對象的alpha透明度。默認值是1(不透明),0代表完全透明(不可見)。
動畫繪製過程的監聽
-
animator.addUpdateListener(new
AnimatorUpdateListener() {
-
@Override
-
public
void onAnimationUpdate(ValueAnimator
arg0) {
-
}
-
});
該方法用來監聽動畫繪製過程中的每一幀的改變,通過這個方法,我們可以在動畫重繪的過程中,實現自己的邏輯。
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator arg0) {
- }
- });
同時修改多個屬性值
當然這個可以使用Animationset來實現,這裏我們使用一種取巧的方法來實現:
-
ObjectAnimator anim = ObjectAnimator.ofFloat(view,
"xxx",
1.0F,
0.0F)
-
.setDuration(500);
-
anim.start();
-
anim.addUpdateListener(new
AnimatorUpdateListener() {
-
@Override
-
public
void onAnimationUpdate(ValueAnimator
animation) {
-
floatcVal = (Float) animation.getAnimatedValue();
-
view.setAlpha(cVal);
-
view.setScaleX(cVal);
-
view.setScaleY(cVal);
-
}
-
});
我們可以監聽一個並不存在的屬性,而在監聽動畫更新的方法中,去修改view的屬性,監聽一個不存在的屬性的原因就是,我們只需要動畫的變化值,通過這個值,我們自己來實現要修改的效果,實際上,更直接的方法,就是使用ValueAnimator來實現,其實ObjectAnimator就是ValueAnimator的子類,這個在下面會具體講到。
- ObjectAnimator anim = ObjectAnimator.ofFloat(view, "xxx", 1.0F, 0.0F)
- .setDuration(500);
- anim.start();
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- floatcVal = (Float) animation.getAnimatedValue();
- view.setAlpha(cVal);
- view.setScaleX(cVal);
- view.setScaleY(cVal);
- }
- });
爲不具有get/set方法的屬性提供修改方法
Google在應用層爲我們提供了2種解決方法,一種是通過自己寫一個包裝類,來爲該屬性提供get/set方法,還有一種是通過ValueAnimator來實現,ValueAnimator的方法我們在下面會具體講解,這裏講解下如何使用自定義的包裝類來給屬性提供get/set方法。
包裝類
-
private
static
class WrapperView {
-
private View mTarget;
-
-
public WrapperView(View target) {
-
mTarget = target;
-
}
-
-
public
int getWidth() {
-
return mTarget.getLayoutParams().width;
-
}
-
-
public
void setWidth(int
width) {
-
mTarget.getLayoutParams().width = width;
-
mTarget.requestLayout();
-
}
-
}
使用方法:
- private static class WrapperView {
- private View mTarget;
- public WrapperView(View target) {
- mTarget = target;
- }
- public int getWidth() {
- return mTarget.getLayoutParams().width;
- }
- public void setWidth(int width) {
- mTarget.getLayoutParams().width = width;
- mTarget.requestLayout();
- }
- }
- ViewWrapper wrapper = new ViewWrapper(mButton);
- ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
這樣就間接給他加上了get/set方法,從而可以修改其屬性實現動畫效果。
多動畫效果的另一種實現方法——propertyValuesHolder
-
public
void propertyValuesHolder(View
view) {
-
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha",
1f,
-
0f, 1f);
-
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX",
1f,
-
0, 1f);
-
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY",
1f,
-
0, 1f);
-
ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ)
-
.setDuration(1000).start();
-
}
- public void propertyValuesHolder(View view) {
- PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,
- 0f, 1f);
- PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,
- 0, 1f);
- PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,
- 0, 1f);
- ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ)
- .setDuration(1000).start();
- }
ValueAnimator
說簡單點,ValueAnimator就是一個數值產生器,他本身不作用於任何一個對象,但是可以對產生的值進行動畫處理。
-
ValueAnimator animator = ValueAnimator.ofFloat(0,
100);
-
animator.setTarget(view);
-
animator.setDuration(1000).start();
-
animator.addUpdateListener(new
AnimatorUpdateListener() {
-
@Override
-
public
void onAnimationUpdate(ValueAnimator
animation) {
-
Float value = (Float) animation.getAnimatedValue();
-
imageView.setTranslationY(value);
-
}
-
});
通過這個動畫我們可以發現,和我們在上面提供的使用ObjectAnimator的方法很像,的確,我前面說這個纔是專業的寫法,就是這個原因,動畫生成的原理就是通過差值器計算出來的一定規律變化的數值作用到對象上來實現對象效果的變化,因此我們可以使用ObjectAnimator來生成這些數,然後在動畫重繪的監聽中,完成自己的效果。
ValueAnimator是計算動畫過程中變化的值,包含動畫的開始值,結束值,持續時間等屬性。但並沒有把這些計算出來的值應用到具體的對象上面,所以也不會有什麼的動畫顯示出來。要把計算出來的值應用到對象上,必須爲ValueAnimator註冊一個監聽器ValueAnimator.AnimatorUpdateListener,該監聽器負責更新對象的屬性值。在實現這個監聽器的時候,可以通過getAnimatedValue()的方法來獲取當前幀的值。
ValueAnimator封裝了一個TimeInterpolator,TimeInterpolator定義了屬性值在開始值與結束值之間的插值方法。ValueAnimator還封裝了一個TypeAnimator,根據開始、結束值與TimeIniterpolator計算得到的值計算出屬性值。ValueAnimator根據動畫已進行的時間跟動畫總時間(duration)的比計算出一個時間因子(0~1),然後根據TimeInterpolator計算出另一個因子,最後TypeAnimator通過這個因子計算出屬性值,例如在10ms時(total
40ms):
首先計算出時間因子,即經過的時間百分比:t=10ms/40ms=0.25
經插值計算(inteplator)後的插值因子:大約爲0.15,如果使用了AccelerateDecelerateInterpolator,計算公式爲(input即爲時間因子):
(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
最後根據TypeEvaluator計算出在10ms時的屬性值:0.15*(40-0)=6pixel。如果使用TypeEvaluator爲FloatEvaluator,計算方法爲 :
-
public
Float evaluate(float fraction,
Number startValue, Number endValue) {
-
float startFloat = startValue.floatValue();
-
return startFloat + fraction * (endValue.floatValue() - startFloat);
-
}
參數分別爲上一步的插值因子,開始值與結束值。
- ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
- animator.setTarget(view);
- animator.setDuration(1000).start();
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- Float value = (Float) animation.getAnimatedValue();
- imageView.setTranslationY(value);
- }
- });
- public Float evaluate(float fraction, Number startValue, Number endValue) {
- float startFloat = startValue.floatValue();
- return startFloat + fraction * (endValue.floatValue() - startFloat);
- }
ValueAnimator與ObjectAnimator實例
-
package
com.example.animtest;
-
-
import
android.animation.TypeEvaluator;
-
import
android.animation.ValueAnimator;
-
import
android.animation.ValueAnimator.AnimatorUpdateListener;
-
import
android.app.Activity;
-
import
android.graphics.PointF;
-
import
android.os.Bundle;
-
import
android.util.DisplayMetrics;
-
import
android.view.View;
-
import
android.view.Window;
-
import
android.view.WindowManager;
-
import
android.view.animation.BounceInterpolator;
-
import
android.view.animation.LinearInterpolator;
-
import
android.widget.ImageView;
-
-
public
class AnimateFreeFall
extends Activity {
-
-
private
int screenHeight;
-
private
int screenWidth;
-
private ImageView imageView;
-
-
@Override
-
protected
void onCreate(Bundle savedInstanceState)
{
-
super.onCreate(savedInstanceState);
-
requestWindowFeature(Window.FEATURE_NO_TITLE);
-
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
-
WindowManager.LayoutParams.FLAG_FULLSCREEN);
-
setContentView(R.layout.animate_free_fall);
-
DisplayMetrics metrics =
new DisplayMetrics();
-
getWindowManager().getDefaultDisplay().getMetrics(metrics);
-
screenHeight = metrics.heightPixels;
-
screenWidth = metrics.widthPixels;
-
-
imageView = (ImageView) findViewById(R.id.im);
-
}
-
-
public
void clean(View view) {
-
imageView.setTranslationX(0);
-
imageView.setTranslationY(0);
-
}
-
-
public
void freefall(View view) {
-
final ValueAnimator animator = ValueAnimator.ofFloat(0,
screenHeight
-
- imageView.getHeight());
-
animator.setTarget(view);
-
animator.setInterpolator(new
BounceInterpolator());
-
animator.setDuration(1000).start();
-
animator.addUpdateListener(new
AnimatorUpdateListener() {
-
-
@Override
-
public
void onAnimationUpdate(ValueAnimator
animation) {
-
Float value = (Float) animation.getAnimatedValue();
-
imageView.setTranslationY(value);
-
}
-
});
-
}
-
-
public
void parabola(View view) {
-
ValueAnimator animator = ValueAnimator.ofObject(
-
new TypeEvaluator<PointF>() {
-
-
@Override
-
public PointF evaluate(float
fraction, PointF arg1,
-
PointF arg2) {
-
PointF p =
new PointF();
-
p.x = fraction * screenWidth;
-
p.y = fraction * fraction *
0.5f * screenHeight * 4f
-
*
0.5f;
-
return p;
-
}
-
},
new PointF(0,
0));
-
animator.setDuration(800);
-
animator.setInterpolator(new
LinearInterpolator());
-
animator.start();
-
animator.addUpdateListener(new
AnimatorUpdateListener() {
-
-
@Override
-
public
void onAnimationUpdate(ValueAnimator
animator) {
-
PointF p = (PointF) animator.getAnimatedValue();
-
imageView.setTranslationX(p.x);
-
imageView.setTranslationY(p.y);
-
}
-
});
-
}
-
}
- package com.example.animtest;
- import android.animation.TypeEvaluator;
- import android.animation.ValueAnimator;
- import android.animation.ValueAnimator.AnimatorUpdateListener;
- import android.app.Activity;
- import android.graphics.PointF;
- import android.os.Bundle;
- import android.util.DisplayMetrics;
- import android.view.View;
- import android.view.Window;
- import android.view.WindowManager;
- import android.view.animation.BounceInterpolator;
- import android.view.animation.LinearInterpolator;
- import android.widget.ImageView;
- public class AnimateFreeFall extends Activity {
- private int screenHeight;
- private int screenWidth;
- private ImageView imageView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- setContentView(R.layout.animate_free_fall);
- DisplayMetrics metrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(metrics);
- screenHeight = metrics.heightPixels;
- screenWidth = metrics.widthPixels;
- imageView = (ImageView) findViewById(R.id.im);
- }
- public void clean(View view) {
- imageView.setTranslationX(0);
- imageView.setTranslationY(0);
- }
- public void freefall(View view) {
- final ValueAnimator animator = ValueAnimator.ofFloat(0, screenHeight
- - imageView.getHeight());
- animator.setTarget(view);
- animator.setInterpolator(new BounceInterpolator());
- animator.setDuration(1000).start();
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- Float value = (Float) animation.getAnimatedValue();
- imageView.setTranslationY(value);
- }
- });
- }
- public void parabola(View view) {
- ValueAnimator animator = ValueAnimator.ofObject(
- new TypeEvaluator<PointF>() {
- @Override
- public PointF evaluate(float fraction, PointF arg1,
- PointF arg2) {
- PointF p = new PointF();
- p.x = fraction * screenWidth;
- p.y = fraction * fraction * 0.5f * screenHeight * 4f
- * 0.5f;
- return p;
- }
- }, new PointF(0, 0));
- animator.setDuration(800);
- animator.setInterpolator(new LinearInterpolator());
- animator.start();
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- PointF p = (PointF) animator.getAnimatedValue();
- imageView.setTranslationX(p.x);
- imageView.setTranslationY(p.y);
- }
- });
- }
- }
自定義TypeEvaluator傳入的泛型可以根據自己的需求,自己設計個Bean。
動畫事件的監聽
通過監聽這個事件在屬性的值更新時執行相應的操作,對於ValueAnimator一般要監聽此事件執行相應的動作,不然Animation沒意義(但是可用於計時),在ObjectAnimator(繼承自ValueAnimator)中會自動更新屬性,所以不必監聽。在函數中會傳遞一個ValueAnimator參數,通過此參數的getAnimatedValue()取得當前動畫屬性值。
PS:根據應用動畫的對象或屬性的不同,可能需要在onAnimationUpdate函數中調用invalidate()函數刷新視圖通過動畫的各種狀態,我們可以監聽動畫的各種狀態。
-
ObjectAnimator
anim =
ObjectAnimator.ofFloat(view, "alpha",
0.5f);
-
anim.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) {
-
}
-
});
-
anim.start();
可以看見,API提供了開始、重複、結束、取消等各種狀態的監聽,同時,API還提供了一種簡單的監聽方法,可以不用監聽所有的事件:
-
anim.addListener(new
AnimatorListenerAdapter() {
-
@Override
-
public
void onAnimationEnd(Animator
animation) {
-
}
-
});
通過AnimatorListenerAdapter來選擇你需要監聽的事件
- ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", 0.5f);
- anim.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) {
- }
- });
- anim.start();
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- }
- });
動畫監聽的實例應用
-
package
com.example.animtest;
-
-
import
android.animation.Animator;
-
import
android.animation.AnimatorListenerAdapter;
-
import
android.animation.ObjectAnimator;
-
import
android.app.Activity;
-
import
android.os.Bundle;
-
import
android.view.View;
-
import
android.widget.ImageView;
-
-
public
class AnimateMoveInSecond
extends Activity {
-
-
private ImageView imageView;
-
-
@Override
-
protected
void onCreate(Bundle savedInstanceState)
{
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.animate_move_in_second);
-
imageView = (ImageView) findViewById(R.id.imageView1);
-
}
-
-
public
void doit(View view) {
-
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,
"alpha",
-
1.0f, 0f);
-
animator.setDuration(1000);
-
animator.start();
-
animator.addListener(new
AnimatorListenerAdapter() {
-
-
@Override
-
public
void onAnimationEnd(Animator
animation) {
-
super.onAnimationEnd(animation);
-
ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,
-
"alpha", 0f,
1.0f);
-
animator.setDuration(1000);
-
animator.start();
-
imageView.setTranslationY(400);
-
}
-
});
-
}
-
}
- package com.example.animtest;
- import android.animation.Animator;
- import android.animation.AnimatorListenerAdapter;
- import android.animation.ObjectAnimator;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.ImageView;
- public class AnimateMoveInSecond extends Activity {
- private ImageView imageView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.animate_move_in_second);
- imageView = (ImageView) findViewById(R.id.imageView1);
- }
- public void doit(View view) {
- ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha",
- 1.0f, 0f);
- animator.setDuration(1000);
- animator.start();
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- ObjectAnimator animator = ObjectAnimator.ofFloat(imageView,
- "alpha", 0f, 1.0f);
- animator.setDuration(1000);
- animator.start();
- imageView.setTranslationY(400);
- }
- });
- }
- }
AnimatorSet
AnimatorSet用於實現多個動畫的協同作用。效果如下:
- ObjectAnimator animator1 = ObjectAnimator.ofFloat(imageView, "scaleX",
- 1f, 2f);
- ObjectAnimator animator2 = ObjectAnimator.ofFloat(imageView, "scaleY",
- 1f, 2f);
- ObjectAnimator animator3 = ObjectAnimator.ofFloat(imageView,
- "translationY", 0f, 500f);
- AnimatorSet set = new AnimatorSet();
- set.setDuration(1000);
- set.playTogether(animator1, animator2, animator3);
- set.start();
AnimatorSet中有一系列的順序控制方法:playTogether、playSequentially、animSet.play().with()、defore()、after()等。用來實現多個動畫的協同工作方式。
使用xml來創建動畫
屬性動畫於以前的動畫一樣,也支持通過xml文件來創建動畫,下面是一個簡單的例子:
-
<?xml
version="1.0"
encoding="utf-8"?>
-
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
-
android:duration="1000"
-
android:propertyName="scaleX"
-
android:valueFrom="1.0"
-
android:valueTo="2.0"
-
android:valueType="floatType"
>
-
</objectAnimator>
-
public
void scaleX(View view)
-
{
-
// 加載動畫
-
Animator anim = AnimatorInflater.loadAnimator(this,
R.animator.scalex);
-
anim.setTarget(mMv);
-
anim.start();
-
}
- <?xml version="1.0" encoding="utf-8"?>
- <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:duration="1000"
- android:propertyName="scaleX"
- android:valueFrom="1.0"
- android:valueTo="2.0"
- android:valueType="floatType" >
- </objectAnimator>
- public void scaleX(View view)
- {
- // 加載動畫
- Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scalex);
- anim.setTarget(mMv);
- anim.start();
- }
佈局動畫
佈局動畫是指ViewGroup在佈局時產生的動畫效果
LayoutTransition動畫
通過LayoutTransition來實現容器在添加子view的時候的動畫過渡效果:
- package com.example.animtest;
- import android.animation.Animator;
- import android.animation.AnimatorListenerAdapter;
- import android.animation.Keyframe;
- import android.animation.LayoutTransition;
- import android.animation.ObjectAnimator;
- import android.animation.PropertyValuesHolder;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.LinearLayout;
- public class AnimateLayoutTransition extends Activity {
- private LinearLayout ll;
- private LayoutTransition mTransition = new LayoutTransition();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.animate_layout_transition);
- ll = (LinearLayout) findViewById(R.id.ll);
- setupCustomAnimations();
- ll.setLayoutTransition(mTransition);
- }
- public void add(View view) {
- final Button button = new Button(this);
- ll.addView(button);
- button.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- ll.removeView(button);
- }
- });
- }
- // 生成自定義動畫
- private void setupCustomAnimations() {
- // 動畫:CHANGE_APPEARING
- // Changing while Adding
- PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
- PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
- PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0,
- 1);
- PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom",
- 0, 1);
- PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX",
- 1f, 0f, 1f);
- PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY",
- 1f, 0f, 1f);
- final ObjectAnimator changeIn = ObjectAnimator.ofPropertyValuesHolder(
- this, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScaleX,
- pvhScaleY).setDuration(
- mTransition.getDuration(LayoutTransition.CHANGE_APPEARING));
- mTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeIn);
- changeIn.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator anim) {
- View view = (View) ((ObjectAnimator) anim).getTarget();
- // View也支持此種動畫執行方式了
- view.setScaleX(1f);
- view.setScaleY(1f);
- }
- });
- // 動畫:CHANGE_DISAPPEARING
- // Changing while Removing
- Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
- Keyframe kf1 = Keyframe.ofFloat(.9999f, 360f);
- Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
- PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe(
- "rotation", kf0, kf1, kf2);
- final ObjectAnimator changeOut = ObjectAnimator
- .ofPropertyValuesHolder(this, pvhLeft, pvhTop, pvhRight,
- pvhBottom, pvhRotation)
- .setDuration(
- mTransition
- .getDuration(LayoutTransition.CHANGE_DISAPPEARING));
- mTransition
- .setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeOut);
- changeOut.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator anim) {
- View view = (View) ((ObjectAnimator) anim).getTarget();
- view.setRotation(0f);
- }
- });
- // 動畫:APPEARING
- // Adding
- ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 90f,
- 0f).setDuration(
- mTransition.getDuration(LayoutTransition.APPEARING));
- mTransition.setAnimator(LayoutTransition.APPEARING, animIn);
- animIn.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator anim) {
- View view = (View) ((ObjectAnimator) anim).getTarget();
- view.setRotationY(0f);
- }
- });
- // 動畫:DISAPPEARING
- // Removing
- ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "rotationX", 0f,
- 90f).setDuration(
- mTransition.getDuration(LayoutTransition.DISAPPEARING));
- mTransition.setAnimator(LayoutTransition.DISAPPEARING, animOut);
- animOut.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator anim) {
- View view = (View) ((ObjectAnimator) anim).getTarget();
- view.setRotationX(0f);
- }
- });
- }
- }
上面的例子中自定義了 LayoutTransition來修改默認的過渡動畫,如果保持默認則使用系統默認的動畫效果。
過渡的類型一共有四種:
LayoutTransition.APPEARING 當一個View在ViewGroup中出現時,對此View設置的動畫
LayoutTransition.CHANGE_APPEARING當一個View在ViewGroup中出現時,對此View對其他View位置造成影響,對其他View設置的動畫
LayoutTransition.DISAPPEARING當一個View在ViewGroup中消失時,對此View設置的動畫
LayoutTransition.CHANGE_DISAPPEARING當一個View在ViewGroup中消失時,對此View對其他View位置造成影響,對其他View設置的動畫
LayoutTransition.CHANGE 不是由於View出現或消失造成對其他View位置造成影響,然後對其他View設置的動畫。
注意動畫到底設置在誰身上,此View還是其他View。
AnimateLayoutChanges動畫
ViewGroup的xml屬性中有一個默認的animateLayoutChanges屬性,設置該屬性,可以添加ViewGroup增加view的過渡效果:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/ll"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:animateLayoutChanges="true"
- android:orientation="vertical" >
- <Button
- android:id="@+id/button1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="add"
- android:text="Add Button" />
- </LinearLayout>
LayoutAnimation動畫
通過設置LayoutAnimation也同樣可以實現佈局動畫效果,實例如下:
- package com.example.animtest;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.animation.LayoutAnimationController;
- import android.view.animation.ScaleAnimation;
- import android.widget.LinearLayout;
- public class AnimateLayoutAnimation extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.animate_layout_animation);
- LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
- ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1);
- sa.setDuration(2000);
- // 第二個參數dely : the delay by which each child's animation must be offset
- LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
- // 設置顯示的順序 這個必須要在dely不爲0的時候纔有效
- lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
- ll.setLayoutAnimation(lac);
- }
- }
View的animate方法
3.0後android對View也提供了直接作用的動畫API:
-
view.animate().alpha(0).y(100).setDuration(1000)
-
.withStartAction(new
Runnable() {
-
@Override
-
public
void run() {
-
}
-
}).withEndAction(new
Runnable() {
-
@Override
-
public
void run() {
-
runOnUiThread(new
Runnable() {
-
@Override
-
public
void run() {
-
}
-
});
-
}
-
}).start();
- view.animate().alpha(0).y(100).setDuration(1000)
- .withStartAction(new Runnable() {
- @Override
- public void run() {
- }
- }).withEndAction(new Runnable() {
- @Override
- public void run() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- }
- });
- }
- }).start();
Interpolators(插值器)
插值器和估值器,是實現非線性動畫的基礎,瞭解這些東西,才能作出不一樣的動畫效果。所謂的插值器,就是通過一些數學物理公式,計算出一些數值,提供給動畫來使用。就好比我們定義了起始值是0,結束值是100,但是這0到100具體是怎麼變化的呢,這就是插值器產生的結果,線性的,就是勻速增長,加速的,就是按加速度增長。這些增加的算法公式,已經不需要我們來自己設計了,Android內置了7種插值器,基本可以滿足需求,當然你也可以自定新的插值器。
AccelerateInterpolator 加速
Decelerate 減速
AccelerateDecelerateInterpolator 開始,和結尾都很慢,但是,中間加速
AnticipateInterpolator 開始向後一點,然後,往前拋
OvershootInterpolator 往前拋超過一點,然後返回來
AnticipateOvershootInterpolator 開始向後一點,往前拋過點,然後返回來
BounceInterpolator 結束的時候彈一下
LinearInterpolator 默認 勻速
TypeEvalutors (估值器)
根據屬性的開始、結束值與TimeInterpolation計算出的因子計算出當前時間的屬性值,android提供了以下幾個evalutor:
IntEvaluator:屬性的值類型爲int;
FloatEvaluator:屬性的值類型爲float;
ArgbEvaluator:屬性的值類型爲十六進制顏色值;
TypeEvaluator:一個接口,可以通過實現該接口自定義Evaluator。
自定義TypeEvalutor很簡單,只需要實現一個方法,如FloatEvalutor的定義:
-
public
class FloatEvaluator
implements TypeEvaluator {
-
public Object evaluate(float
fraction, Object startValue,
-
Object endValue) {
-
float startFloat = ((Number) startValue).floatValue();
-
return startFloat + fraction
-
* (((Number) endValue).floatValue() - startFloat);
-
}
-
}
根據動畫執行的時間跟應用的Interplator,會計算出一個0~1之間的因子,即evalute函數中的fraction參數。
- public class FloatEvaluator implements TypeEvaluator {
- public Object evaluate(float fraction, Object startValue,
- Object endValue) {
- float startFloat = ((Number) startValue).floatValue();
- return startFloat + fraction
- * (((Number) endValue).floatValue() - startFloat);
- }
- }
KeyFrame
keyFrame是一個 時間/值 對,通過它可以定義一個在特定時間的特定狀態,即關鍵幀,而且在兩個keyFrame之間可以定義不同的Interpolator,就好像多個動畫的拼接,第一個動畫的結束點是第二個動畫的開始點。KeyFrame是抽象類,要通過ofInt(),ofFloat(),ofObject()獲得適當的KeyFrame,然後通過PropertyValuesHolder.ofKeyframe獲得PropertyValuesHolder對象,如以下例子:
-
Keyframe kf0 = Keyframe.ofInt(0,
400);
-
Keyframe kf1 = Keyframe.ofInt(0.25f,
200);
-
Keyframe kf2 = Keyframe.ofInt(0.5f,
400);
-
Keyframe kf4 = Keyframe.ofInt(0.75f,
100);
-
Keyframe kf3 = Keyframe.ofInt(1f,
500);
-
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("width",
kf0, kf1, kf2, kf4, kf3);
-
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(btn2, pvhRotation);
-
rotationAnim.setDuration(2000);
上述代碼的意思爲:設置btn對象的width屬性值使其:
開始時 Width=400
動畫開始1/4時 Width=200
動畫開始1/2時 Width=400
動畫開始3/4時 Width=100
動畫結束時 Width=500
第一個參數爲時間百分比,第二個參數是在第一個參數的時間時的屬性值。
定義了一些Keyframe後,通過PropertyValuesHolder類的方法ofKeyframe一個PropertyValuesHolder對象,然後通過ObjectAnimator.ofPropertyValuesHolder獲得一個Animator對象。
用下面的代碼可以實現同樣的效果(上述代碼時間值是線性,變化均勻):
- Keyframe kf0 = Keyframe.ofInt(0, 400);
- Keyframe kf1 = Keyframe.ofInt(0.25f, 200);
- Keyframe kf2 = Keyframe.ofInt(0.5f, 400);
- Keyframe kf4 = Keyframe.ofInt(0.75f, 100);
- Keyframe kf3 = Keyframe.ofInt(1f, 500);
- PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf4, kf3);
- ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(btn2, pvhRotation);
- rotationAnim.setDuration(2000);
- ObjectAnimator oa=ObjectAnimator.ofInt(btn2, "width", 400,200,400,100,500);
- oa.setDuration(2000);
- oa.start();