一般情況下,動畫并不會影響我們應用的業(yè)務邏輯,但沒有動畫會讓我們的應用顯得過于死板,沒有活力與競爭力。
android中的動畫大致可以分為三類:view動畫,幀動畫與屬性動畫。
我們先來看view動畫:
view動畫表示繼承自Animation的一類動畫,包括對view的平移,縮放,透明度,旋轉。即:TranslateAnimation,ScaleAnimation,AlphaAnimation,RotateAnimation。這四種動畫可以可以通過xml文件定義,如在res/anim/animation.xml定義,也可以在代碼中寫,但在xml中比在代碼中寫要方便的多。當然除了這四種類型,我們也可以通過AnimationSet類將這四種動畫組合使用。我們就以xml下定義為例:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@[package:]anim/interpolator_resource"
android:shareInterpolator=["true" | "false"] >
<alpha
android:fromAlpha="float"
android:toAlpha="float" />
<scale
android:fromXScale="float"
android:toXScale="float"
android:fromYScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float" />
<translate
android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float" />
<rotate
android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float" />
</set>
android:shareInterpolator屬性表示動畫是否共享同一個插值器,interpolator表示插值器,也可以通過setInterpolator()方法傳入一個插值器對象,下面簡單介紹一些常見的插值器:
AccelerateDecelerateInterpolator
先加速再減速。這是默認的 Interpolator,也就是說如果你不設置的話,那么動畫將會使用這個 Interpolator。
LinearInterpolator
勻速。
AccelerateInterpolator
持續(xù)加速。在整個動畫過程中,一直在加速,直到動畫結束的一瞬間,直接停止。
DecelerateInterpolator
持續(xù)減速直到 0。動畫開始的時候是最高速度,然后在動畫過程中逐漸減速,直到動畫結束的時候恰好減速到 0。
AnticipateInterpolator
先回拉一段位移再進行正常動畫軌跡。
OvershootInterpolator
動畫會超過目標區(qū)域再彈回。
AnticipateOvershootInterpolator
AnticipateInterpolator與OvershootInterpolator的結合版:開始前回拉,最后超過一段位移然后彈回。
BounceInterpolator
在目標區(qū)域來回彈跳,直至停止。
CycleInterpolator
動畫可以提前到達目標區(qū)域,也可以到達目標區(qū)域后回彈,由構造函數參數決定。
PathInterpolator
Path路徑的坐標表示動畫完成度 / 時間完成度曲線。這是一個自定義的動畫模型,根據你想要達到的效果來自定義動畫完成度與時間的關系。但不允許同一時間有兩個動畫完成度出現,否則會報fc。
FastOutLinearInInterpolator
類似于AccelerateInterpolator,不過它在開始加速過程中速度會較快,加速度較大,不過差別也比較小,很難發(fā)現區(qū)別。
FastOutSlowInInterpolator
類似于AccelerateDecelerateInterpolator,先加速,后減速,在加速與減速階段加速度都較大。
LinearOutSlowInInterpolator
類似于DecelerateInterpolator,開始達到速度最大值,然后一路減速運行,不過初始速度會比較高,減速較快。
插值器就簡單介紹到這里。
加載xml動畫也比較簡單,通過AnimationUtils類來實現:
AnimationUtils.loadAnimation(this, R.anim.animation);
通過setAnimationListener可以為Animation設置監(jiān)聽器:
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
如果我們想讓view停留在動畫結束的地方, 可以通過調用fillAfter()方法來實現:
animation.setFillAfter(true);
不過這里需要注意的是,雖然view看上去停留在了動畫結束的地方,但當我們觸發(fā)view的監(jiān)聽事件時,發(fā)現并沒有起作用,而只有在點擊原來的位置才能觸發(fā)監(jiān)聽事件,這并不是android的bug,這是因為view動畫只是view的影像,而view本身的屬性并沒有發(fā)生改變。
另外如果當你在動畫結束需要設置view的visibility屬性時,需要先清除動畫,否則將不起作用。
view.clearAnimation();
幀動畫:
幀動畫表示view切換過程中的動畫效果,可以是activity之間的切換,也可以是view的切換。activity之間的切換可能是用的比較多的一類幀動畫,當我們需要銷毀或啟動一個activity時,在finish()或startActivity()后面跟上如下代碼:
overridePendingTransition(enterAnim, exitAnim);
就能實現activity之間的切換動畫,比如,如果要實現右滑的效果:
slide_left_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="-100%p"
android:toXDelta="0">
</translate>
</set>
slide_right_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="0"
android:toXDelta="100%p">
</translate>
</set>
在finish()或startActivity()后面增加:
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
如果進行view之間的動畫切換,我們可以借助AnimationDrawable來實現,比如:
frame_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@android:color/white" android:duration="500"></item>
<item android:drawable="@android:color/holo_green_dark" android:duration="500"></item>
<item android:drawable="@android:color/black" android:duration="500"></item>
</animation-list>
在activity通過如下調用:
view = findViewById(R.id.activity_main);
view.setBackgroundResource(R.drawable.frame_animation);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AnimationDrawable aniDrawable = (AnimationDrawable) view.getBackground();
aniDrawable.start();
}
});
我這里只是更改了view的背景色,當然你也可以將android:drawable屬性設為你喜歡的圖像。
接下來讓我們看一下屬性動畫:
在我們的應用開發(fā)中,屬性動畫一般是用的最多的一類動畫,屬性動畫區(qū)別于其它動畫的最主要特征是view的屬性值的改變。屬性動畫是android 3.0(API 11)之后的產物,相對傳統(tǒng)動畫能實現更豐富的動畫效果。如果android 3.0之前的版本也想使用屬性動畫,可以采用nineoldandroids開源動畫庫,網址是:http://nineoldandroids.com.屬性動畫從實現上由簡到難可大致分為ViewAnimator,ObjectAnimator,ValueAnimator。
我們首先來看ViewAnimator,這里主要用到的是它的子類ViewPropertyAnimator。ViewPropertyAnimator動畫的使用來源于view的四大轉換,即我們熟悉的平移,縮放,旋轉以及透明度。即:
view.setTranslationX(float translationX);
view.setTranslationY(float translationY);
view.setTranslationZ(float translationZ);
view.setScaleX(float scaleX);
view.setScaleY(float scaleY);
view.setRotation(float rotation);
view.setRotationX(float rotationX);
view.setRotationY(float rotationY);
view.setAlpha(float alpha);
使用上也很簡單,一行代碼即可搞定:
view.animate().translationX(500);
也可以基于之前的位置進行動畫處理:
view.animate().translationYBy(500);
其它的動畫也類似,就不舉例了,當然除了這些單一的動畫,還可以多種動畫同時進行,比如這種寫法:
view.animate()
.translationX(500)
.scaleY(4)
.rotation(360)
.alpha(0.5f);
屬性動畫一般可以同時設置兩種類型的監(jiān)聽器:
view.animate().setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
表示了動畫的執(zhí)行周期回調。
view.animate().setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
}
});
表明動畫在執(zhí)行過程中的回調,每一幀(不確切的說為10ms)就會進行一次回調,根據這個回調我們就可以在動畫執(zhí)行過程中處理一些事情。
相比于ViewPropertyAnimator,ObjectAnimator功能更強大,但同樣操作起來也更復雜,ObjectAnimator通過不斷更改屬性值而實現的,我們先看看ObjectAnimator如何實現動畫效果。
比如我們需要不斷更改ProgressBar的進度條來實現動畫效果,我們就可以這樣寫:
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
ObjectAnimator animator = ObjectAnimator.ofInt(progressBar, "progress", 0, 100);
animator.start();
這里有幾點需要注意:
1.由于progress是值是int型,所以我們需要用到ofInt方法來生成ObjectAnimator方法,ofFloat()也一樣,而顏色卻不能這樣使用,因為我們知道通過argb來表示顏色的話每個Byte都有不同的意義,而不能通過簡簡單單的int值去處理,所以android給我們提供了ofArgb()專門用于處理顏色變幻的。
2."progress"屬性必須存在,而且必須有提供好的setProgress()與getProgress()(當構造中沒有傳入progress初始值時必須提供get方法,否則會fc)。
另外我們也可以通過使用AnimatorSet讓我們的動畫同時進行,如:
AnimatorSet aniSet = new AnimatorSet();
aniSet.playTogether(animator1, animator2);
也可以按一定順序執(zhí)行:
AnimatorSet aniSet = new AnimatorSet();
aniSet.playSequentially(animator2, animator1);
也可以使用PropertyValuesHolder:
PropertyValuesHolder scaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 0, 1);
PropertyValuesHolder scaleYHolder = PropertyValuesHolder.ofFloat("scaleY", 0, 1);
PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofFloat("alpha", 0, 1);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, scaleXHolder, scaleYHolder, alphaHolder);
animator.start();
也可以是同一個動畫的不同階段,即動畫完成度/動畫時間:
Keyframe step1 = Keyframe.ofFloat(0, 0);
Keyframe step2 = Keyframe.ofFloat(0.5f, 100);
Keyframe step3 = Keyframe.ofFloat(1, 50);
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", step1, step2, step3);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder);
animator.start();
當然除了系統(tǒng)為我們提供好的屬性外,我們也可以在控件中定義自己的屬性。這個時候就要用到,比如:
ObjectAnimator animator = ObjectAnimator.ofObject(view, "position",
new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
animator.setDuration(1000);
animator.start();
由于position屬性是一個坐標點,即PointF對象,系統(tǒng)并沒有為我們提供具體的接口,這個時候就需要我們自己來實現,通過ofObject,而且我們的自定義view中必須有position屬性與setPosition方法。由于實現的是ofObject方法,所以這里我們需要實現自己的估值器,實現TypeEvaluator()接口。
private class PointFEvaluator implements TypeEvaluator<PointF> {
// 重寫 evaluate() 方法,讓 PointF 可以作為屬性來做動畫
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF outPointF = new PointF();
outPointF.x = startValue.x + (endValue.x - startValue.x) * fraction;
outPointF.y = startValue.y + (endValue.y - startValue.y) * fraction;
return outPointF;
}
}
最后讓我們來看看ValueAnimator,ValueAnimator是ObjectAnimator的父類,但ValueAnimator并不能指定動畫的目標,所以如果我們想要使用ValueAnimator達到動畫的效果,那么我們可以通過實現TypeEvaluator接口,在每一幀動畫到來時去invalidate()目標view達到動畫的效果。
接下來要說的一些動畫我們可能用的比較少,但對于我們工程師來說,卻能讓我們的應用看起來更炫。
第一個要說的是LayoutAnimation,LayoutAnimation主要作用于ViewGroup,它也是一種view動畫,常常作用在ListView或GridView上,這樣當子view出場時就有了一定的動效。
layout_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="500"
android:animationOrder="normal"
android:animation="@android:anim/fade_in"/>
main.xml
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
...
android:id="@+id/listview"
android:layoutAnimation="@anim/layout_animation"
...
/>
除了在xml指定動畫效果外,還可以在代碼中指定:
listview = findViewById(R.id.listview);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.layout_animation);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
接下來說的是gif動畫,也就是播放一張gif圖片。這個我也是參考的網上的博文,就不講了,這里有一篇不錯的文章大家可以看一下:http://blog.csdn.net/guolin_blog/article/details/11100315
最后再講一下3D動畫,3D動畫主要包含三類,旋轉,平移,移動相機,3D動畫主要用到的是Camera這個類,它的原理是通過Camera對view進行投影從而達到3d的效果,Camera使用的是3D坐標系,仍然以左上角為坐標原點(0,0,0),這點與Canvas保持一致,x族為右正左負,y族上正下負,z族垂直于屏幕外正內負。旋轉方向為以沿x族上方從屏幕外向里為x正向旋轉,沿y族順時針方向為y正向旋轉,沿z族從右向左為z正向旋轉。Camera默認坐標位置為(0,0,-576),我們可以通過setLocation調整它的位置。
我們通過一個例子來看如何實現3D效果:
首先需要自定義一個3D動畫:
public class Rotate3DAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private boolean mReverse;
private Camera mCamera;
private float scale = 2.5f;
/**
* 創(chuàng)建一個繞y軸旋轉的3D動畫效果,旋轉過程中具有深度調節(jié),可以指定旋轉中心。
*
* @param fromDegrees 起始時角度
* @param toDegrees 結束時角度
* @param centerX 旋轉中心x坐標
* @param centerY 旋轉中心y坐標
* @param depthZ 最遠到達的z軸坐標
* @param reverse true 表示由從0到depthZ,false相反
*/
public Rotate3DAnimation(float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
mReverse = interpolatedTime < 0.5?true:false;
// 調節(jié)深度
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
// 繞y軸旋轉
camera.rotateX(degrees);
camera.getMatrix(matrix);
camera.restore();
// 修正失真,主要修改 MPERSP_0 和 MPERSP_1
float[] mValues = new float[9];
matrix.getValues(mValues); //獲取數值
mValues[6] = mValues[6]/scale; //數值修正
mValues[7] = mValues[7]/scale; //數值修正
matrix.setValues(mValues); //重新賦值
// 調節(jié)中心點
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
在Activity調用:
Rotate3DAnimation rotate3D = new Rotate3DAnimation(0, 90, view.getWidth()/2, view.getHeight()/2, 0, false);
rotate3D.setDuration(1000);
rotate3D.setFillAfter(true);
view.startAnimation(rotate3D);
這里要注意的是Camera對動畫的執(zhí)行是反序的,不過我們可以通過preTranslate或postTranslate指定動畫順序,即先將view平移到原點,然后繞x旋轉,最后將旋轉后的結果在平移到原來的位置。
到這里所有動畫相關的內容就結束了,如果有什么問題,希望給我留言。