Android動(dòng)畫(huà)機(jī)制全解析
本文著重講解Android3.0后推出的屬性動(dòng)畫(huà)框架Property Animation——Animator。
產(chǎn)生原因3.0之前已有的動(dòng)畫(huà)框架——Animation存在一些局限性, Animation框架定義了透明度,旋轉(zhuǎn),縮放和位移幾種常見(jiàn)的動(dòng)畫(huà),而且控制的是整個(gè)View,實(shí)現(xiàn)原理是每次繪制視圖時(shí)View所在的ViewGroup中的drawChild函數(shù)獲取該View的Animation的Transformation值,然后調(diào)用canvas.concat(transformToApply.getMatrix()),通過(guò)矩陣運(yùn)算完成動(dòng)畫(huà)幀,如果動(dòng)畫(huà)沒(méi)有完成,繼續(xù)調(diào)用invalidate()函數(shù),啟動(dòng)下次繪制來(lái)驅(qū)動(dòng)動(dòng)畫(huà),動(dòng)畫(huà)過(guò)程中的幀之間間隙時(shí)間是繪制函數(shù)所消耗的時(shí)間,可能會(huì)導(dǎo)致動(dòng)畫(huà)消耗比較多的CPU資源,最重要的是,動(dòng)畫(huà)改變的只是顯示,并不能相應(yīng)事件。
而在Animator框架中使用最多的是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator進(jìn)行更精細(xì)化控制,只控制一個(gè)對(duì)象的一個(gè)屬性值,多個(gè)ObjectAnimator組合到AnimatorSet形成一個(gè)動(dòng)畫(huà)。而且ObjectAnimator能夠自動(dòng)驅(qū)動(dòng),可以調(diào)用setFrameDelay(longframeDelay)設(shè)置動(dòng)畫(huà)幀之間的間隙時(shí)間,調(diào)整幀率,減少動(dòng)畫(huà)過(guò)程中頻繁繪制界面,而在不影響動(dòng)畫(huà)效果的前提下減少CPU資源消耗。因此,Anroid推出的強(qiáng)大的屬性動(dòng)畫(huà)框架,基本可以實(shí)現(xiàn)所有的動(dòng)畫(huà)效果。
強(qiáng)大的原因因?yàn)閷傩詣?dòng)畫(huà)框架操作的是真實(shí)的屬性值,直接變化了對(duì)象的屬性,因此可以很靈活的實(shí)現(xiàn)各種效果,而不局限于以前的4種動(dòng)畫(huà)效
ObjectAnimator是屬性動(dòng)畫(huà)框架中最重要的實(shí)行類,創(chuàng)建一個(gè)ObjectAnimator只需通過(guò)他的靜態(tài)工廠類直接返回一個(gè)ObjectAnimator對(duì)象。傳的參數(shù)包括一個(gè)對(duì)象和對(duì)象的屬性名字,但這個(gè)屬性必須有g(shù)et和set函數(shù),內(nèi)部會(huì)通過(guò)java反射機(jī)制來(lái)調(diào)用set函數(shù)修改對(duì)象屬性值。還包括屬性的初始值,最終值,還可以調(diào)用setInterpolator設(shè)置曲線函數(shù)。
ObjectAnimator實(shí)例ObjectAnimator.ofFloat(view, "rotationX", 0.0F, 360.0F).setDuration(1000).start();
這個(gè)例子很簡(jiǎn)單,針對(duì)view的屬性rotationX進(jìn)行持續(xù)時(shí)間為1000ms的0到360的角度變換。
PS:可操縱的屬性參數(shù):x/y;scaleX/scaleY;rotationX/ rotationY;transitionX/ transitionY等等。
PS:X是View最終的位置、translationX為最終位置與布局時(shí)初始位置的差。所以若就用translationX即為在原來(lái)基礎(chǔ)上移動(dòng)多少,X為最終多少。getX()的值為getLeft()與getTranslationX()的和。
動(dòng)畫(huà)繪制過(guò)程的監(jiān)聽(tīng)animator.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator arg0) {}});
該方法用來(lái)監(jiān)聽(tīng)動(dòng)畫(huà)繪制過(guò)程中的每一幀的改變,通過(guò)這個(gè)方法,我們可以在動(dòng)畫(huà)重繪的過(guò)程中,實(shí)現(xiàn)自己的邏輯。
同時(shí)修改多個(gè)屬性值當(dāng)然這個(gè)可以使用Animationset來(lái)實(shí)現(xiàn),這里我們使用一種取巧的方法來(lái)實(shí)現(xiàn):
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "xxx", 1.0F, 0.0F).setDuration(500);anim.start();anim.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {floatcVal = (Float) animation.getAnimatedValue();view.setAlpha(cVal);view.setScaleX(cVal);view.setScaleY(cVal);}});
我們可以監(jiān)聽(tīng)一個(gè)并不存在的屬性,而在監(jiān)聽(tīng)動(dòng)畫(huà)更新的方法中,去修改view的屬性,監(jiān)聽(tīng)一個(gè)不存在的屬性的原因就是,我們只需要?jiǎng)赢?huà)的變化值,通過(guò)這個(gè)值,我們自己來(lái)實(shí)現(xiàn)要修改的效果,實(shí)際上,更直接的方法,就是使用ValueAnimator來(lái)實(shí)現(xiàn),其實(shí)ObjectAnimator就是ValueAnimator的子類,這個(gè)在下面會(huì)具體講到。
為不具有g(shù)et/set方法的屬性提供修改方法Google在應(yīng)用層為我們提供了2種解決方法,一種是通過(guò)自己寫(xiě)一個(gè)包裝類,來(lái)為該屬性提供get/set方法,還有一種是通過(guò)ValueAnimator來(lái)實(shí)現(xiàn),ValueAnimator的方法我們?cè)谙旅鏁?huì)具體講解,這里講解下如何使用自定義的包裝類來(lái)給屬性提供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();}}
使用方法:
ViewWrapper wrapper = new ViewWrapper(mButton);ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
這樣就間接給他加上了get/set方法,從而可以修改其屬性實(shí)現(xiàn)動(dòng)畫(huà)效果。
多動(dòng)畫(huà)效果的另一種實(shí)現(xiàn)方法——propertyValuesHolderpublic 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
說(shuō)簡(jiǎn)單點(diǎn),ValueAnimator就是一個(gè)數(shù)值產(chǎn)生器,他本身不作用于任何一個(gè)對(duì)象,但是可以對(duì)產(chǎn)生的值進(jìn)行動(dòng)畫(huà)處理。
ValueAnimator animator = ValueAnimator.ofFloat(0, 100);animator.setTarget(view);animator.setDuration(1000).start();animator.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Float value = (Float) animation.getAnimatedValue();imageView.setTranslationY(value);}});
通過(guò)這個(gè)動(dòng)畫(huà)我們可以發(fā)現(xiàn),和我們?cè)谏厦嫣峁┑氖褂肙bjectAnimator的方法很像,的確,我前面說(shuō)這個(gè)才是專業(yè)的寫(xiě)法,就是這個(gè)原因,動(dòng)畫(huà)生成的原理就是通過(guò)差值器計(jì)算出來(lái)的一定規(guī)律變化的數(shù)值作用到對(duì)象上來(lái)實(shí)現(xiàn)對(duì)象效果的變化,因此我們可以使用ObjectAnimator來(lái)生成這些數(shù),然后在動(dòng)畫(huà)重繪的監(jiān)聽(tīng)中,完成自己的效果。
ValueAnimator是計(jì)算動(dòng)畫(huà)過(guò)程中變化的值,包含動(dòng)畫(huà)的開(kāi)始值,結(jié)束值,持續(xù)時(shí)間等屬性。但并沒(méi)有把這些計(jì)算出來(lái)的值應(yīng)用到具體的對(duì)象上面,所以也不會(huì)有什么的動(dòng)畫(huà)顯示出來(lái)。要把計(jì)算出來(lái)的值應(yīng)用到對(duì)象上,必須為ValueAnimator注冊(cè)一個(gè)監(jiān)聽(tīng)器ValueAnimator.AnimatorUpdateListener,該監(jiān)聽(tīng)器負(fù)責(zé)更新對(duì)象的屬性值。在實(shí)現(xiàn)這個(gè)監(jiān)聽(tīng)器的時(shí)候,可以通過(guò)getAnimatedValue()的方法來(lái)獲取當(dāng)前幀的值。
ValueAnimator封裝了一個(gè)TimeInterpolator,TimeInterpolator定義了屬性值在開(kāi)始值與結(jié)束值之間的插值方法。ValueAnimator還封裝了一個(gè)TypeAnimator,根據(jù)開(kāi)始、結(jié)束值與TimeIniterpolator計(jì)算得到的值計(jì)算出屬性值。ValueAnimator根據(jù)動(dòng)畫(huà)已進(jìn)行的時(shí)間跟動(dòng)畫(huà)總時(shí)間(duration)的比計(jì)算出一個(gè)時(shí)間因子(0~1),然后根據(jù)TimeInterpolator計(jì)算出另一個(gè)因子,最后TypeAnimator通過(guò)這個(gè)因子計(jì)算出屬性值,例如在10ms時(shí)(total 40ms):
首先計(jì)算出時(shí)間因子,即經(jīng)過(guò)的時(shí)間百分比:t=10ms/40ms=0.25
經(jīng)插值計(jì)算(inteplator)后的插值因子:大約為0.15,如果使用了AccelerateDecelerateInterpolator,計(jì)算公式為(input即為時(shí)間因子):
(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
最后根據(jù)TypeEvaluator計(jì)算出在10ms時(shí)的屬性值:0.15*(40-0)=6pixel。如果使用TypeEvaluator為FloatEvaluator,計(jì)算方法為 :
public Float evaluate(float fraction, Number startValue, Number endValue) {float startFloat = startValue.floatValue();return startFloat + fraction * (endValue.floatValue() - startFloat);}
參數(shù)分別為上一步的插值因子,開(kāi)始值與結(jié)束值。
ValueAnimator與ObjectAnimator實(shí)例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;@Overrideprotected 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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Float value = (Float) animation.getAnimatedValue();imageView.setTranslationY(value);}});}public void parabola(View view) {ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator<PointF>() {@Overridepublic 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() {@Overridepublic void onAnimationUpdate(ValueAnimator animator) {PointF p = (PointF) animator.getAnimatedValue();imageView.setTranslationX(p.x);imageView.setTranslationY(p.y);}});}}
效果如下圖:
有一點(diǎn)需要注意的是,由于ofInt,ofFloat等無(wú)法使用,我們自定義了一個(gè)TypeValue,每次根據(jù)當(dāng)前時(shí)間返回一個(gè)PointF對(duì)象,(PointF和Point的區(qū)別就是x,y的單位一個(gè)是float,一個(gè)是int point float的意思)PointF中包含了x,y的當(dāng)前位置,然后在更新監(jiān)聽(tīng)中更新。
自定義TypeEvaluator傳入的泛型可以根據(jù)自己的需求,自己設(shè)計(jì)個(gè)Bean。
動(dòng)畫(huà)事件的監(jiān)聽(tīng)通過(guò)監(jiān)聽(tīng)這個(gè)事件在屬性的值更新時(shí)執(zhí)行相應(yīng)的操作,對(duì)于ValueAnimator一般要監(jiān)聽(tīng)此事件執(zhí)行相應(yīng)的動(dòng)作,不然Animation沒(méi)意義(但是可用于計(jì)時(shí)),在ObjectAnimator(繼承自ValueAnimator)中會(huì)自動(dòng)更新屬性,所以不必監(jiān)聽(tīng)。在函數(shù)中會(huì)傳遞一個(gè)ValueAnimator參數(shù),通過(guò)此參數(shù)的getAnimatedValue()取得當(dāng)前動(dòng)畫(huà)屬性值。
PS:根據(jù)應(yīng)用動(dòng)畫(huà)的對(duì)象或?qū)傩缘牟煌?,可能需要在onAnimationUpdate函數(shù)中調(diào)用invalidate()函數(shù)刷新視圖通過(guò)動(dòng)畫(huà)的各種狀態(tài),我們可以監(jiān)聽(tīng)動(dòng)畫(huà)的各種狀態(tài)。
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", 0.5f);anim.addListener(new AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {}@Overridepublic void onAnimationCancel(Animator animation) {}});anim.start();
可以看見(jiàn),API提供了開(kāi)始、重復(fù)、結(jié)束、取消等各種狀態(tài)的監(jiān)聽(tīng),同時(shí),API還提供了一種簡(jiǎn)單的監(jiān)聽(tīng)方法,可以不用監(jiān)聽(tīng)所有的事件:
anim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {}});
通過(guò)AnimatorListenerAdapter來(lái)選擇你需要監(jiān)聽(tīng)的事件
動(dòng)畫(huà)監(jiān)聽(tīng)的實(shí)例應(yīng)用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;@Overrideprotected 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() {@Overridepublic 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用于實(shí)現(xiàn)多個(gè)動(dòng)畫(huà)的協(xié)同作用。效果如下:
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()等。用來(lái)實(shí)現(xiàn)多個(gè)動(dòng)畫(huà)的協(xié)同工作方式。
使用xml來(lái)創(chuàng)建動(dòng)畫(huà)屬性動(dòng)畫(huà)于以前的動(dòng)畫(huà)一樣,也支持通過(guò)xml文件來(lái)創(chuàng)建動(dòng)畫(huà),下面是一個(gè)簡(jiǎn)單的例子:
<?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){// 加載動(dòng)畫(huà)Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scalex);anim.setTarget(mMv);anim.start();} 布局動(dòng)畫(huà)
布局動(dòng)畫(huà)是指ViewGroup在布局時(shí)產(chǎn)生的動(dòng)畫(huà)效果
LayoutTransition動(dòng)畫(huà)通過(guò)LayoutTransition來(lái)實(shí)現(xiàn)容器在添加子view的時(shí)候的動(dòng)畫(huà)過(guò)渡效果:
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();@Overrideprotected 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() {@Overridepublic void onClick(View arg0) {ll.removeView(button);}});}// 生成自定義動(dòng)畫(huà)private void setupCustomAnimations() {// 動(dòng)畫(huà):CHANGE_APPEARING// Changing while AddingPropertyValuesHolder 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也支持此種動(dòng)畫(huà)執(zhí)行方式了view.setScaleX(1f);view.setScaleY(1f);}});// 動(dòng)畫(huà):CHANGE_DISAPPEARING// Changing while RemovingKeyframe 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);}});// 動(dòng)畫(huà):APPEARING// AddingObjectAnimator 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);}});// 動(dòng)畫(huà):DISAPPEARING// RemovingObjectAnimator 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來(lái)修改默認(rèn)的過(guò)渡動(dòng)畫(huà),如果保持默認(rèn)則使用系統(tǒng)默認(rèn)的動(dòng)畫(huà)效果。
過(guò)渡的類型一共有四種:
LayoutTransition.APPEARING 當(dāng)一個(gè)View在ViewGroup中出現(xiàn)時(shí),對(duì)此View設(shè)置的動(dòng)畫(huà)
LayoutTransition.CHANGE_APPEARING當(dāng)一個(gè)View在ViewGroup中出現(xiàn)時(shí),對(duì)此View對(duì)其他View位置造成影響,對(duì)其他View設(shè)置的動(dòng)畫(huà)
LayoutTransition.DISAPPEARING當(dāng)一個(gè)View在ViewGroup中消失時(shí),對(duì)此View設(shè)置的動(dòng)畫(huà)
LayoutTransition.CHANGE_DISAPPEARING當(dāng)一個(gè)View在ViewGroup中消失時(shí),對(duì)此View對(duì)其他View位置造成影響,對(duì)其他View設(shè)置的動(dòng)畫(huà)
LayoutTransition.CHANGE 不是由于View出現(xiàn)或消失造成對(duì)其他View位置造成影響,然后對(duì)其他View設(shè)置的動(dòng)畫(huà)。
注意動(dòng)畫(huà)到底設(shè)置在誰(shuí)身上,此View還是其他View。
AnimateLayoutChanges動(dòng)畫(huà)ViewGroup的xml屬性中有一個(gè)默認(rèn)的animateLayoutChanges屬性,設(shè)置該屬性,可以添加ViewGroup增加view的過(guò)渡效果:
<?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" ><Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="add"android:text="Add Button" /></LinearLayout>LayoutAnimation動(dòng)畫(huà)
通過(guò)設(shè)置LayoutAnimation也同樣可以實(shí)現(xiàn)布局動(dòng)畫(huà)效果,實(shí)例如下:
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 {@Overrideprotected 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);// 第二個(gè)參數(shù)dely : the delay by which each child's animation must be offsetLayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);// 設(shè)置顯示的順序 這個(gè)必須要在dely不為0的時(shí)候才有效lac.setOrder(LayoutAnimationController.ORDER_NORMAL);ll.setLayoutAnimation(lac);}} View的animate方法
3.0后android對(duì)View也提供了直接作用的動(dòng)畫(huà)API:
view.animate().alpha(0).y(100).setDuration(1000).withStartAction(new Runnable() {@Overridepublic void run() {}}).withEndAction(new Runnable() {@Overridepublic void run() {runOnUiThread(new Runnable() {@Overridepublic void run() {}});}}).start(); Interpolators(插值器)
插值器和估值器,是實(shí)現(xiàn)非線性動(dòng)畫(huà)的基礎(chǔ),了解這些東西,才能作出不一樣的動(dòng)畫(huà)效果。所謂的插值器,就是通過(guò)一些數(shù)學(xué)物理公式,計(jì)算出一些數(shù)值,提供給動(dòng)畫(huà)來(lái)使用。就好比我們定義了起始值是0,結(jié)束值是100,但是這0到100具體是怎么變化的呢,這就是插值器產(chǎn)生的結(jié)果,線性的,就是勻速增長(zhǎng),加速的,就是按加速度增長(zhǎng)。這些增加的算法公式,已經(jīng)不需要我們來(lái)自己設(shè)計(jì)了,Android內(nèi)置了7種插值器,基本可以滿足需求,當(dāng)然你也可以自定新的插值器。
AccelerateInterpolator 加速
Decelerate 減速
AccelerateDecelerateInterpolator 開(kāi)始,和結(jié)尾都很慢,但是,中間加速
AnticipateInterpolator 開(kāi)始向后一點(diǎn),然后,往前拋
OvershootInterpolator 往前拋超過(guò)一點(diǎn),然后返回來(lái)
AnticipateOvershootInterpolator 開(kāi)始向后一點(diǎn),往前拋過(guò)點(diǎn),然后返回來(lái)
BounceInterpolator 結(jié)束的時(shí)候彈一下
LinearInterpolator 默認(rèn) 勻速
TypeEvalutors (估值器)根據(jù)屬性的開(kāi)始、結(jié)束值與TimeInterpolation計(jì)算出的因子計(jì)算出當(dāng)前時(shí)間的屬性值,android提供了以下幾個(gè)evalutor:
IntEvaluator:屬性的值類型為int;
FloatEvaluator:屬性的值類型為float;
ArgbEvaluator:屬性的值類型為十六進(jìn)制顏色值;
TypeEvaluator:一個(gè)接口,可以通過(guò)實(shí)現(xiàn)該接口自定義Evaluator。
自定義TypeEvalutor很簡(jiǎn)單,只需要實(shí)現(xiàn)一個(gè)方法,如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);}}
根據(jù)動(dòng)畫(huà)執(zhí)行的時(shí)間跟應(yīng)用的Interplator,會(huì)計(jì)算出一個(gè)0~1之間的因子,即evalute函數(shù)中的fraction參數(shù)。
KeyFramekeyFrame是一個(gè) 時(shí)間/值 對(duì),通過(guò)它可以定義一個(gè)在特定時(shí)間的特定狀態(tài),即關(guān)鍵幀,而且在兩個(gè)keyFrame之間可以定義不同的Interpolator,就好像多個(gè)動(dòng)畫(huà)的拼接,第一個(gè)動(dòng)畫(huà)的結(jié)束點(diǎn)是第二個(gè)動(dòng)畫(huà)的開(kāi)始點(diǎn)。KeyFrame是抽象類,要通過(guò)ofInt(),ofFloat(),ofObject()獲得適當(dāng)?shù)腒eyFrame,然后通過(guò)PropertyValuesHolder.ofKeyframe獲得PropertyValuesHolder對(duì)象,如以下例子:
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);
上述代碼的意思為:設(shè)置btn對(duì)象的width屬性值使其:
開(kāi)始時(shí) Width=400
動(dòng)畫(huà)開(kāi)始1/4時(shí) Width=200
動(dòng)畫(huà)開(kāi)始1/2時(shí) Width=400
動(dòng)畫(huà)開(kāi)始3/4時(shí) Width=100
動(dòng)畫(huà)結(jié)束時(shí) Width=500
第一個(gè)參數(shù)為時(shí)間百分比,第二個(gè)參數(shù)是在第一個(gè)參數(shù)的時(shí)間時(shí)的屬性值。
定義了一些Keyframe后,通過(guò)PropertyValuesHolder類的方法ofKeyframe一個(gè)PropertyValuesHolder對(duì)象,然后通過(guò)ObjectAnimator.ofPropertyValuesHolder獲得一個(gè)Animator對(duì)象。
用下面的代碼可以實(shí)現(xiàn)同樣的效果(上述代碼時(shí)間值是線性,變化均勻):
ObjectAnimator oa=ObjectAnimator.ofInt(btn2, "width", 400,200,400,100,500);oa.setDuration(2000);oa.start();
代碼下載地址:http://download.csdn.net/detail/x359981514/7721651
相關(guān)文章:
1. Python TestSuite生成測(cè)試報(bào)告過(guò)程解析2. 深入了解JAVA 軟引用3. python實(shí)現(xiàn)讀取類別頻數(shù)數(shù)據(jù)畫(huà)水平條形圖案例4. 解決AJAX返回狀態(tài)200沒(méi)有調(diào)用success的問(wèn)題5. JSP之表單提交get和post的區(qū)別詳解及實(shí)例6. 詳解JAVA 強(qiáng)引用7. chat.asp聊天程序的編寫(xiě)方法8. python之cur.fetchall與cur.fetchone提取數(shù)據(jù)并統(tǒng)計(jì)處理操作9. 低版本IE正常運(yùn)行HTML5+CSS3網(wǎng)站的3種解決方案10. python讓函數(shù)不返回結(jié)果的方法
