Android動畫-幀動畫、補間動畫、屬性動畫

幀動畫(Frame Animation)

定義: 幀動畫就是按照順序播放一幀一幀的照片達到動畫的效果。

我們可以看一下實現過程:在drawable目錄下新建frame_list.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="@drawable/frame_animation_1"
        android:duration="300" />
    <item
        android:drawable="@drawable/frame_animation_2"
        android:duration="300" />
    <item
        android:drawable="@drawable/frame_animation_3"
        android:duration="300" />
</animation-list>

具體代碼中使用如下:

@BindView(R.id.image_container)
    ImageView imageContainer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_animation);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.frame_animation_start)
    public void onStartFrameAnimation() {
        imageContainer.setImageResource(R.drawable.frame_list);
        AnimationDrawable animationDrawable = (AnimationDrawable)imageContainer.getDrawable();
        animationDrawable.start();
    }

    @OnClick(R.id.frame_animation_end)
    public void onEndFrameAnimation() {
        AnimationDrawable animationDrawable = (AnimationDrawable)imageContainer.getDrawable();
        animationDrawable.stop();
    }
  1. Activity中使用imageContainer作爲播放的容器
  2. (AnimationDrawable)imageContainer.getDrawable() 能獲取到幀動畫的對象進行播放/停止操作

備註:
幀動畫使用簡單方便,但是注意在比較複雜的時候圖片比較多的時候容易發生oom,因此儘量避免使用尺寸較大的圖片。

補間動畫(Tween Animation)

定義:與前面寫了幀動畫不同,幀動畫是通過連續播放圖片來模擬動畫效果,而補間動畫開發者只需指定動畫開始,以及動畫結束"關鍵幀", 而動畫變化的"中間幀"則由系統計算並補齊!它只是去改變了視覺效果,並不會改變控件本身的屬性。補間動畫包括基本的平移動畫,縮放動畫,旋轉動畫和透明度動畫,我們也可以將這些基本動畫進行組合在一起。

android支持的補間動畫類型

AlphaAnimation:透明度漸變效果,創建時許指定開始以及結束透明度,還有動畫的持續 時間,透明度的變化範圍(0,1),0是完全透明,1是完全不透明;對應<alpha/>標籤!
ScaleAnimation:縮放漸變效果,創建時需指定開始以及結束的縮放比,以及縮放參考點, 還有動畫的持續時間;對應<scale/>標籤!
TranslateAnimation:位移漸變效果,創建時指定起始以及結束位置,並指定動畫的持續 時間即可;對應<translate/>標籤!
RotateAnimation:旋轉漸變效果,創建時指定動畫起始以及結束的旋轉角度,以及動畫 持續時間和旋轉的軸心;對應<rotate/>標籤
AnimationSet:組合漸變,就是前面多種漸變的組合,對應<set/>標籤

每種動畫都可以通過兩種方式去實現,代碼實現和xml文件實現,個人肯定傾向於代碼去做。

xml文件實現

xml文件如下:

translate xmlns:android="http://schemas.android.com/apk/res/android"
   android:duration="3000"
   android:startOffset ="1000"
   android:fillAfter = "false"
   android:repeatMode= "reverse"
   android:repeatCount = "infinite"
   android:interpolator="@android:anim/accelerate_decelerate_interpolator"

   android:fromXDelta="0"
   android:toXDelta="500"
   android:fromYDelta="0"
   android:toYDelta="0">

   <!--duration:動畫時長
   startOffset:動畫延遲開始時間
   fillAfter:動畫結束後是否停留在結束狀態
   repeatMode:重複播放動畫模式restart代表正序重放,reverse代表倒序回放
   repeatCount:重放次數(+1),爲infinite時無限重複
   interpolator:插值器:選擇動畫變化的模式,有勻速,加速,減速,先加速在減速。。。

   以上是所有補間動畫共有的屬性,以下是平移動畫特有的屬性
   fromXDelta:平移動畫在水平方向x初始值
   toXDelta:平移動畫在水平方向x結束值
   fromYDelta:平移動畫在豎直方向y初始值
   toYDelta:平移動畫在豎直方向y結束值-->
</translate>

Activity中實現如下:

    @OnClick(R.id.transfer_animation_xml)
    public void onTransferXML() {
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_translate);
        mImageContainer.startAnimation(animation);
    }

代碼實現

通過代碼寫一個補件動畫也是很簡單的:

    @OnClick(R.id.transfer_animation_java)
    public void onTransferJava() {
        Animation transferAnimation = new TranslateAnimation(0, 500, 0, 0);
        transferAnimation.setRepeatMode(Animation.RESTART);
        transferAnimation.setDuration(3000);
        transferAnimation.setRepeatCount(3);
        mImageContainer.startAnimation(transferAnimation);
    }

動畫結束後 組件的位置:
不改變動畫的屬性,所以最後位置也不會改變。
是否可以組件:
不同的補間動畫可以重疊在一起:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="1000"
        android:fromAlpha="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toAlpha="1" />
    <translate
        android:duration="2000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:startOffset="3000"
        android:toXDelta="500"
        android:toYDelta="0" />
    <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="infinite"
        android:repeatMode="restart"
        android:toDegrees="360" />
    <scale
        android:duration="3000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="4000"
        android:toXScale="0.5"
        android:toYScale="0.5" />
</set>

備註: 以上所有的動畫都可以設置listener監聽其執行過程

屬性動畫(Property Animator)

定義:通過控制view的屬性來實現動畫
 1、爲什麼引入 Property Animator(屬性動畫)    
我提出一個假設:請問大家,如何利用補間動畫來將一個控件的背景色在一分鐘內從綠色變爲紅色?這個效果想必沒辦法僅僅通過改變控件的漸入漸出、移動、旋轉和縮放來實現吧,而這個效果是可以通過 Property Animator 完美實現的 這就是第一個原因:Property Animator 能實現補間動畫無法實現的功能 大家都知道,補間動畫和逐幀動畫統稱爲 View Animation,也就是說這兩個動畫只能對派生自 View 的控件實例起作用;而 Property Animator 則不同,從名字中可以看出屬性動畫,應該是作用於控件屬性的!正因爲屬性動畫能夠只針對控件的某一個屬性來做動畫,所以也就造就了他能單獨改變控件的某一個屬性的值!比如顏色!這就是 Property Animator 能實現補間動畫無法實現的功能的最重要原因。 我們得到了第二點不同:View Animation 僅能對指定的控件做動畫,而 Property Animator 是通過改變控件某一屬性值來做動畫的。 假設我們將一個按鈕從左上角利用補間動畫將其移動到右下角,在移動過程中和移動後,這個按鈕都是不會響應點擊事件的。這是爲什麼呢?因爲補間動畫僅僅轉變的是控件的顯示位置而已,並沒有改變控件本身的值。View Animation 的動畫實現是通過其 Parent View 實現的,在 View 被 drawn 時 Parents View 改變它的繪製參數,這樣雖然 View 的大小或旋轉角度等改變了,但 View 的實際屬性沒變,所以有效區域還是應用動畫之前的區域;我們看到的效果僅僅是系統作用在按鈕上的顯示效果,利用動畫把按鈕從原來的位置移到了右下角,但按鈕內部的任何值是沒有變化的,所以按鈕所捕捉的點擊區域仍是原來的點擊區域。(下面會舉例來說明這個問題) 這就得到了第三點不同:補間動畫雖能對控件做動畫,但並沒有改變控件內部的屬性值。而 Property Animator 則是恰恰相反,Property Animator 是通過改變控件內部的屬性值來達到動畫效果的

ValueAnimator

簡單的實現,實際場景中可以在listener中去更新view的屬性。 下面的代碼是通過改變屬性讓view向右下方移動400像素

@OnClick(R.id.value_animation)
    public void onValueAnimation() {
        int left = mImageContainer.getLeft();
        int right = mImageContainer.getRight();
        int top = mImageContainer.getTop();
        int bottom = mImageContainer.getBottom();
        Log.d(TAG, "onValueAnimation: left=" + mImageContainer.getLeft() + "    right=" + mImageContainer.getRight()
                + " top=" + mImageContainer.getTop() + "    bottom=" + mImageContainer.getBottom());
        ValueAnimator animator = ValueAnimator.ofInt(0,400);
        animator.setDuration(4000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (Integer) animation.getAnimatedValue();
                Log.d(TAG, "onAnimationUpdate: " + value);
                mImageContainer.layout(left + value, top +value, right + value, bottom + value);
            }
        });
        animator.start();
    }

ObjectAnimator

ObjectAnimator 是對ValueAnimator的封裝與擴展,上面的例子開發者還需要關心view的update的過程,ObjectAnimator則是將該過程封裝起來對開發者不可見。
 ValueAnimator 有個缺點,就是隻能對數值對動畫計算。我們要想對哪個控件操作,需要監聽動畫過程,在監聽中對控件操作。這樣使用起來相比補間動畫而言就相對比較麻煩。 爲了能讓動畫直接與對應控件相關聯,以使我們從監聽動畫過程中解放出來,谷歌的開發人員在 ValueAnimator 的基礎上,又派生了一個類 ObjectAnimator; 由於 ObjectAnimator 是派生自 ValueAnimator 的,所以 ValueAnimator 中所能使用的方法,在 ObjectAnimator 中都可以正常使用。 但 ObjectAnimator 也重寫了幾個方法,比如 ofInt(),ofFloat()等。我們先看看利用 ObjectAnimator 重寫的 ofFloat 方法如何實現一個動畫:(改變透明度)

改變透明度的屬性動畫

    @OnClick(R.id.object_animation_simple)
    public void onObjectAnimationSimple() {
        ObjectAnimator animator = ObjectAnimator.ofFloat(mPointView,"alpha",1,0,1);
        animator.setDuration(2000);
        animator.start();
    }

通過set函數改變自定義view屬性

public class MyPointView extends View {

    private int mRadius;

    public MyPointView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(300,300, mRadius, paint);
        super.onDraw(canvas);
    }

    public int getPointRadius(){
        return 50;
    }

    public void setPointRadius(int radius){
        mRadius = radius;
        invalidate();
    }
}

        // activity中測試代碼
    @OnClick(R.id.object_animation_selfview)
    public void onObjectAnimationSelfView() {
        ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius",100);
        animator.setDuration(2000);
        animator.start();
    }

性能對比

代碼地址: https://github.com/lsfzlj/AndroidTestNeil/tree/develop

參考:
ValueAnimator 基本使用
Android動畫之幀動畫,補間動畫和屬性動畫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章