Android動畫實現包括視圖動畫以及屬性動畫。其中比較新穎的自然是5.X系統下的矢量圖動畫,這個小編也僅僅只是做了初步的瞭解,畢竟連矢量圖的生成還沒有掌握好,並且漂亮的矢量圖也必須藉助工具來實現。本次內容主要是根據“拋物線運動”的個人實現思路來展開描述的,最終實現肯定是各有方案的。
1.開發中常用的動畫框架:
- AlphaAnimation 透明度動畫
- RotateAnimation 旋轉動畫
- TranslateAnimation 位移動畫
- ScaleAnimation 縮放動畫
基於以上動畫的實現可以實現集合動畫,主要是利用AnimationSet.addAnimation();將其組合起來實現。動畫執行期間(包括動畫的啓動,取消,結束以及循環)的監聽可以是.setAnimationListener(new Animation.AnimationListener(){};當然,由於我們往往更多關注的是動畫結束時候的處理,所以可以直接用.setAnimationListener(new Animation.AnimationListenerAdapter(){};
2.屬性動畫分析
到這裏得理解的一點是,既然通過以上的動畫框架接口已實現很好的視圖動畫效果了,那爲什麼還要去研究屬性動畫呢,原因是其動畫框架僅能實現顯示效果,但無法響應其他事件。故Android3.0之後更多的是用AnimationSet與ObjectAnimator(個體動畫屬性的更改)配合使用。其中ObjectAnimator常用的動畫屬性值有
- translationX、translationY:控制視圖的偏移位置(距離左上角)
- rotation、rotationX、rotationY:控制視圖的2D、3D旋轉
- scaleX、scaleY:控制視圖水平或者垂直方向的縮放
- pivotX、pivotY:控制視圖旋轉或者縮放的支點位置
- x、y:控制視圖的最終位置
- alpha:控制視圖的透明度
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 500);
animator.setDuration(1000);
animator.start();
注意,以上代碼實現的是view在x軸方向上移動至距離左上角水平位置爲500px的地方,而不是在原基礎上向x軸正方向移動500px。如果我們考慮實現多個動畫屬性能夠同時進行,那麼可以藉助PropertyValueHolder實現,如下代碼片段就實現了view在水平偏移的同時進行了垂直方向上的位置偏移。
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 500);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("translationY", 500);
ObjectAnimator.ofPropertyValuesHolder(view, pvh1, pvh2).setDuration(1000).start();
在這裏如果我們希望動畫的集合能夠有一定的順序實現,而不是單一的併發進行。那麼除了給每個獨立的動畫設置動畫監聽以外我們可以繼續用AnimatorSet(注意:這裏不是AnimationSet)。其可以通過playogether()、playSequentially()、animSet.Play().with()、befor()、after()分別控制動畫的工作方式,從而可好的控制動畫的播放順序。如下實現view同時執行位移,旋轉以及縮放的動畫效果。其他方法可自行嘗試。
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 500);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "rotationY", 0, 180);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleX", 1.5f);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(animator1, animator2, animator3);
set.start();
除了通過以代碼的形式設置屬性動畫以外,其實我們還習慣通過.xml來實現其一樣的效果。如下所示實現視圖的透明漸變和引用。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true" >
<alpha
android:duration="1000"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator" />
</set>
Animator anim = AnimatorInflater.loadAnimator(R.drawable.animXml);
anim.setTarget(view);
anim.start;
3.佈局動畫
表示小編基本上沒用過佈局動畫,所謂佈局動畫就是在ViewGroup添加子View時所呈現出來的一個動畫過渡效果。在ViewGroup的.xml當中可以直接設置屬性android:animateLayoutChanges=”true”來啓動漸顯的過渡效果。如果想自定義過渡效果可以類似以下代碼實現:
Animator anim = AnimatorInflater.loadAnimator(R.drawable.animXml);// 定義動畫內容
LayoutAnimationController lac = new LayoutAnimationController(anim );
/*
* LayoutAnimationController.ORDER_RANDOM 隨機
* LayoutAnimationController.ORDER_NORMAL 順序
* LayoutAnimationController.ORDER_REVERSE 反序
* /
lac.setOrder(LayoutAnimationController.ORDER_RANDOM);
lac.setDelay(0.5f);
view.setLayoutAnimation(lac);
4.插值器Interpolators
對於插值器,不得不說這纔是個狠角色。因爲小編的拋物線運動軌跡就是通過插值器的速率變換來實現的。可能有些人還不瞭解插值器是什麼,其實最常見的插值器就是我們在定義動畫時,所引用到的加速減速以及彈回等改變動畫運行狀態的工具。而Interpolators主要包含以下幾種插值器。
Interpolator對象 | 資源ID | 功能作用 |
---|---|---|
AccelerateDecelerateInterpolator | @android:anim/accelerate_decelerate_interpolator | 先加速再減速 |
AccelerateInterpolator | @android:anim/accelerate_interpolator | 加速 |
AnticipateInterpolator | @android:anim/anticipate_interpolator | 先回退一小步然後加速前進 |
AnticipateOvershootInterpolator | @android:anim/anticipate_overshoot_interpolator | 在上一個基礎上超出終點一小步再回到終點 |
BounceInterpolator | @android:anim/bounce_interpolator | 最後階段彈球效果 |
CycleInterpolator | @android:anim/cycle_interpolator | 週期運動 |
DecelerateInterpolator | @android:anim/decelerate_interpolator | 減速 |
LinearInterpolator | @android:anim/linear_interpolator | 勻速 |
OvershootInterpolator | @android:anim/overshoot_interpolator | 快速到達終點並超出一小步最後回到終點 |
而每一種插值器自然而然的也都是繼承自Interpolators實現,並重寫其核心方法public float getInterpolation(float input){};該函數參數input值爲插值器x軸方向上0.0f到1.0f的變換過程。對應的返回值即代表當前的動畫運行速率。根據這一點,我們只要友好地編上一條自定義速率變換方程就可以輕鬆地實現類似拋物線的動畫了。當然,這種實現比較抽象,前提是必須先理解速率變換給動畫運行所帶來的影響,比如說當速率是負數的時候,會改變動畫的運動方向。下面給到小編自己通過AnticipateInterpolator來實現view呈類似拋物線運動的效果及代碼。
爲了方便理解,在瞭解代碼具體實現之前請先了解相關AnticipateInterpolator速率變換軌跡如下。具體可參照 http://my.oschina.net/banxi/blog/135633?fromerr=uv67kzf9#OSC_h2_7 (包括其他插值器對應的速率軌跡圖詳細註解)。
@OnClick({R.id.reddot_btn_parabola})
private void mOnClick(View view) {
switch (view.getId()) {
case R.id.reddot_btn_parabola:
// 每點擊一次按鈕就給父容器mMainRly(RelativeLayout)添加一個小球
final ImageView img = getClickDot(view, 30);
mMainRly.addView(img);
// 獲取底下購物車的位置
final PointF end = getViewPointF(mShopCartImg);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(img, "x", end.x);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(img, "y", end.y);
animator2.setInterpolator(new AnticipateInterpolator(3.0f));
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(800);
animatorSet.playTogether(animator1, animator2);
animatorSet.start();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 小球運行結束後從父容器中移除
mMainRly.removeView(img);
}
});
break;
}
}
/**
* @param v 被點擊的視圖(用於採集球的位置)
* @param size 球體大小
* @return 根據位置返回新的球
*/
private ImageView getClickDot(View v, int size) {
ImageView dotIv = new ImageView(this);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size, size);
dotIv.setLayoutParams(params);
dotIv.setX(v.getX() + v.getWidth() / 2 - size / 2);
dotIv.setY(v.getY() + v.getHeight() / 2 - size / 2);
dotIv.setImageResource(R.drawable.vector_reddot_small);
return dotIv;
}
/**
* @param view 取點視圖
* @return 獲取視圖座標點
*/
private PointF getViewPointF(View view) {
PointF pointF = new PointF();
pointF.x = view.getX() + view.getWidth() / 2;
pointF.y = view.getY() + view.getHeight() / 2;
return pointF;
}
5.自定義動畫
自定義動畫效果繼承自Animation,需要重寫initialize();以及applyTransformation();兩個方法,其中initialize();應用於動畫的一些屬性初始化,而applyTransformation();纔是自定義動畫的核心過程。具體的實現案例“3D翻轉”效果可參考http://chroya.iteye.com/blog/828094