天天看點

Android開發藝術探索筆記 第七章 動畫深入分析

android動畫分為三種 View動畫 幀動畫  屬性動畫  其中幀動畫也屬于View動畫 不過和常見的View旋轉、平移、縮放、透明度的表現形式不同

7.1.1View動畫的種類

Android開發藝術探索筆記 第七章 動畫深入分析

使用View動畫首先需要建立xml檔案這個檔案的路徑為res/anim/filename.xml ,描述檔案有固定的文法

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android"

    android:shareInterpolator="true">

    <alpha

        android:fromAlpha="10.0"

        android:toAlpha="10.0" />

    <scale

        android:fromXScale="10dp"

        android:fromYScale="10dp"

        android:pivotX="10"

        android:pivotY="10"

        android:toXScale="10dp"

        android:toYScale="10dp" />

    <translate

        android:fromXDelta="10"

        android:fromYDelta="10"

        android:toXDelta="10"

        android:toYDelta="10" />

    <rotate

        android:fromDegrees="10"

        android:pivotX="10"

        android:pivotY="10"

        android:toDegrees="10" />

</set>

從上邊的文法來看,View的動畫既可以是單個動畫也可以由一系列的動畫組成

<set>标簽表示動畫集合,對應AnimationSet類它包含很多個類有兩個屬性:

android:interpolator :表示動畫集合所使用的內插補點器,內插補點器影響動畫的速度,比如非勻速動畫就需要內插補點器來制作動畫的過程,這個屬性可以不指定,預設為加速減速內插補點器

android:shareInterpolator:表示集合中的動畫是否合計和使用一個內插補點器,如果集合不指定內插補點器,那麼子動畫就需要單獨的指定所需要的內插補點器了

< translate>标簽表示平移動畫:

  • android:fromXDelta

表示x的起始值,比如0

  • android:fromYDelta

表示y的結束值,比如100

  • android:toXDelta

表示y的起始值

  • android:toYDelta

表示y的結束值

< scale>标簽表示的是縮放動畫

android:fromXScale

水準方向縮放的起始值,比如0.5

android:fromYScale

豎直方向縮放的起始值

android:pivotX

縮放軸點的x坐标,它會影響縮放的效果

android:pivotY

縮放軸點的y坐标,它會影響縮放的效果

android:toXScale

水準方向縮放的結束值,比如1.2

android:toYScale

豎直方向縮放的起始值

<routate>表示動畫的旋轉,對應RotateAnimation,它可以使View具有旋轉的動畫效果

android:fromDegrees

旋轉開始的角度,比如0

android:toDegrees

旋轉結束的角度,比如180

android:pivotX

旋轉軸點的x

android:pivotY

旋轉軸點的y

在旋轉中也有軸的概念,他也會影響到旋轉的效果,軸點扮演者旋轉軸的角色,view圍繞着軸點進行旋轉,預設情況下在view的中心,考慮到一種情況,view圍繞自己的中心,和圍繞左上角進行90度顯然是不同的軌迹

< alpha>表示透明動畫。對應的AlphaAnimation

  • android:fromAlpha

表示透明度的起始值,比如0.1

  • android:toAlpha

上面都隻是很簡單的介紹了XM格式,具體的使用方法還是看文檔,我們還有一些常用的屬性如下

  • android:duration

動畫的時間

  • android:fillAfter

動畫結束之後是否停留在結束的位置

除了在xml中建立動畫 還可以通過代碼實作 但是對于View動畫來說建議采用XML來定義動畫,這是因為XML的可讀性更好,淡然在實際開發中使用xml也是第一選擇

7.1.2自定義View動畫

除了系統提供的四種View動畫外,我們還可以自定義View動畫自定義動畫,在實際開發過程中基本上用不到

7.1.3幀動畫

幀動畫就是順序播放的一組圖檔,系統提供了一個AnimationDrawable類來實作幀動畫

在xml中定義

7.2View動畫的使用場景

view動畫還可以在一些特殊的場景使用 比如在ViewGroup中可以控制子元素的出場效果,實作不同activity之間的切換效果

7.2.1 LayoutAnimation 作用于ViewGroup為ViewGroup制定一個動畫這樣他的子元素出場的時候就具有這種動畫了,這種效果常被用于listview中

7.2.2 Activity的切換效果 

啟動一個

startActivity(new Intent(MainActivity.this,OneActivity.class));

//這是activity的跳轉動畫

overridePendingTransition(R.anim.animation,R.anim.anim_layout);

退出一個

@Override

    public void onBackPressed() {

        super.onBackPressed();

        overridePendingTransition(R.anim.animation, R.anim.anim_layout);

    }

7.3屬性動畫

屬性動畫可以對任何隊形做動畫們甚至沒有對象也可以,屬性動畫效果得到加強,不再像View動畫支援4種屬性動畫有ValueAnimator,ObjectAnimator,AnimatorSet; ObjectAnimator繼承自ValueAnimator,而AnimatorSet可以定義一組動畫一般我們都用ObjectAnimator來聲明一個ValueAnimator在ObjectAnimator聲明屬性變化值。

屬性動畫可以對任意對象的屬性進行動畫,而不僅僅是View 動畫的預設時間是300 預設幀率是10

7.3.1使用屬性動畫

(1)改變一個對象的translationY屬性,讓其沿着Y軸向上平移一個時間,該動畫在預設的時間完成,

ObjectAnimator.ofFloat(iv_icon, "translationY", -iv_icon.getHeight()).start();

(2)改變一個對象的背景顔色值,典型的就是改變view的背景,下面的動畫是讓view的背景從0xffff8080到0xff8080ff,動畫會無限循環和反轉

ValueAnimator valueAnimator =

        ObjectAnimator.ofInt(ll_content, "backgroundColor", 0xFFFF8080, 0xFF8080FF);

valueAnimator.setDuration(3000);

valueAnimator.setEvaluator(new ArgbEvaluator());

valueAnimator.setRepeatCount(ValueAnimator.INFINITE);

valueAnimator.setRepeatMode(ValueAnimator.REVERSE);

valueAnimator.start();

(3)動畫集合,5s内對view的旋轉,平移,縮放和透明度進行改變

實作組合動畫功能主要需要借助AnimatorSet這個類,這個類提供了一個play()方法,如果我們向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator)将會傳回一個AnimatorSet.Builder的執行個體,AnimatorSet.Builder中包括以下四個方法:

  • after(Animator anim)   将現有動畫插入到傳入的動畫之後執行
  • after(long delay)   将現有動畫延遲指定毫秒後執行
  • before(Animator anim)   将現有動畫插入到傳入的動畫之前執行
  • with(Animator anim)   将現有動畫和傳入的動畫同時執行

好的,有了這四個方法,我們就可以完成組合動畫的邏輯了,那麼比如說我們想要讓TextView先從螢幕外移動進螢幕,然後開始旋轉360度,旋轉的同時進行淡入淡出操作,就可以這樣寫:

1

2

3

4

5

6

7

ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, 

"translationX"

, -500f, 0f); 

ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, 

"rotation"

, 0f, 360f); 

ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, 

"alpha"

, 1f, 0f, 1f); 

AnimatorSet animSet = 

new

AnimatorSet(); 

animSet.play(rotate).with(fadeInOut).after(moveIn); 

animSet.setDuration(

5000

); 

animSet.start(); 

 可以看到,這裡我們先是把三個動畫的對象全部建立出來,然後new出一個AnimatorSet對象之後将這三個動畫對象進行播放排序,讓旋轉和淡入淡出動畫同時進行,并把它們插入到了平移動畫的後面,最後是設定動畫時長以及啟動動畫。

當然也可以直接使用playTogether方法将所有的動畫操作放入一個集合中

AnimatorSet set = new AnimatorSet();

set.playTogether(

        ObjectAnimator.ofFloat(iv_icon, "rotationX", 0, 360),  中心x軸進行3d旋轉

        ObjectAnimator.ofFloat(iv_icon, "rotationY", 0, 180),中心y軸進行3d旋轉

        ObjectAnimator.ofFloat(iv_icon, "rotation", 0, -90),中心進行平面旋轉

        ObjectAnimator.ofFloat(iv_icon, "translationX", 0, 90),x軸進行平移

        ObjectAnimator.ofFloat(iv_icon, "translationY", 0, 90),y軸進行平移

        ObjectAnimator.ofFloat(iv_icon, "scaleX", 0, 1.5f),,x軸進行縮放

        ObjectAnimator.ofFloat(iv_icon, "scaleY", 0, 0.5f),y軸進行縮放

        ObjectAnimator.ofFloat(iv_icon, "alpha", 0, 2.5f, 1) 透明度變化

);

set.setDuration(3000).start();

屬性動畫的各個參數是比較好了解的,我們簡單來說下他們之間的含義

propertyName:表示屬性動畫作用對象的屬性的名稱

duration:表示動畫的時長

valueFrom:表示屬性的起始值

valueTo:表示屬性的結束值

startOffset:表示動畫的延遲時間,當動畫開始後,需要延遲多少毫秒才會真正的播放

repeatCount:表示動畫的重複次數

repeatMode:表示動畫的重複模式

valueType:表示propertyName有兩個屬性有int和float兩個可選項,分别表示屬性的類型,和浮點型,另外,如果所制定的是顔色類型,那麼就不需要指定propertyName,系統會自動對顔色類型進行處理

實際開發中一般使用代碼動态建立屬性動畫

ValueAnimator、 ObjectAnimator、 AnimatorSet都可以使用addListener添加監聽事件,生成四個方法,如果你覺得每次實作四種方法,太磨叽,那麼AnimatorListenerAdapter也可以實作你的邏輯,他可以實作你想實作的某一個方法。      

7.3.4 對任意的屬性做動畫

這裡最一個需求,就是給buttion設定一個動畫,讓他的寬度從目前的變成500px,這個可以用view動畫來搞定,但是你仔細想想,view不能對寬高變化,所有我麼可以使用屬性動畫,

我們會發現button确實拉伸了

但是書中卻說不能改變

看下大佬的分享

下面對屬性動畫的原理:屬性動畫要求動畫作用在對象提供的get/set方法,屬性動畫根據外界傳遞的該屬性的初始值和最終值,以動畫效果多次去set,每次傳遞的set方法的值都不一樣,确切來說是随着時間的時間推移,所傳遞的值越來越接近最終值,總結一下,我們對object的屬性abc做動畫,如果想讓動畫生效,要同時滿足兩個條件:

(1)object必須要提供set方法,如果動畫的時候沒有傳遞初始值,那麼我們還要提供get方法,因為系統要去取abc的屬性(如果這條不滿意,程式直接Crash)

(2)object的set方法對abc所做的改變必須通過某種方法反應,比如帶來UI的改變(如果這條不滿足,動畫無效果但是不會Crash)

以上條件缺一不可,那麼為什麼我們對button的width屬性做動畫沒有效果,這是因為button内部雖然提供了get/set方法,但是這個set方法并不是改變視圖大小,他是textview新添加的方法,view是沒有這個setWidth方法的,由于button繼承了textview,所有button也就有了set方法,下面看一下這個get/get的源碼

    @android.view.RemotableViewMethod

    public void setWidth(int pixels) {

        mMaxWidth = mMinWidth = pixels;

        mMaxWidthMode = mMinWidthMode = PIXELS;

        requestLayout();

        invalidate();

    }

    @ViewDebug.ExportedProperty(category = "layout")

    public final int getWidth() {

        return mRight - mLeft;

    }

從上述的源碼可以看出,get的确是擷取view的寬度,而set是textview的專屬方法,他的作用不是設定view的寬度,而是設定textview的最大寬度和最小寬度,這個和textview的寬度不死一個東西,具體來說,textview的寬度對應XML中的android:layout_width,而textview還有一個屬性android:width,這個就對應了setwidth,總之textview和button的set/get幹的不是同一件事,通過set無法改變控件的寬度,是以對width做屬性動畫沒有效果,對于屬性動畫的兩個條件來說,本例中的動畫隻滿足了第一個條件,我們有三個解決辦法:

給你的對象增加set/get方法,前提是你有權限的話

用這個類來包裝原始對象,間接提供get/set方法

采用ValueAnimator,監聽動畫過程自己去實作

我們來具體的實作下這三個解決辦法

1.給你的對象增加set/get方法,前提是你有權限的話

這個的意思很好了解,如果你有權限的話,加個set/get方法就搞定了,但是很多時候我們沒有權限去這麼做,比如本文開頭所提到的問題,你無法給button加上一個合乎要求的setwidth方法,因為這個是Android SDK内部實作的,這個方法很簡單,但是往往是不可行的,這裡就不對其進行更多的分析了

2.用這個類來包裝原始對象,間接提供get/set方法

    private void performAnimate() {

        ViewWrapper viewWrapper = new ViewWrapper(btn);

        ObjectAnimator.ofInt(viewWrapper, "width", 500).setDuration(1000).start();

    }

    private static class ViewWrapper {

        private View mTarget;

        public ViewWrapper(View mTarget) {

            this.mTarget = mTarget;

        }

        public int getWidth() {

            return mTarget.getLayoutParams().width;

        }

        public void setWidth(int width) {

            mTarget.getLayoutParams().width = width;

            mTarget.requestLayout();

        }

    }

上述代碼在1000ms中寬度增加到500,為了達到這個效果我們寫了一個包裝類去提供方法,這樣也就完美的實作了

3.采用ValueAnimator,監聽動畫過程自己去實作

首先說下什麼是ValueAnimator,ValueAnimator本身不作用于任何對象,也就是說直接使用它沒有任何的效果,他可以對一個值做動畫,然後我們監聽這個過程,在過程中修改我們對象的屬性值,這樣就相當于我們的對象做了動畫,下面我們用例子來說明

    private void performAnimator(final View target, final int start, final int end) {

        ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            //持有一個IntEvaluator對象,友善下面估值的時候使用

            private IntEvaluator mEvaluator = new IntEvaluator();

            @Override

            public void onAnimationUpdate(ValueAnimator animation) {

                //獲得目前動畫的進度值,整形1-100之間

                int currentValue = (int) animation.getAnimatedValue();

                //獲得目前進度占整個動畫之間的比例,浮點0-1之間

                float fraction = animation.getAnimatedFraction();

                //直接使用整形估值器,通過比例計算寬度,然後再設定給按鈕

                target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);

                target.requestLayout();

            }

        });

        valueAnimator.setDuration(5000).start();

    }

上面的代碼的效果和剛才的viewwrapper是一樣的,關于ValueAnimator還要再說一下,拿上來的例子來說,他會在5s内将一個數1變成100,然後動畫的每一幀會回調的每一幀onAnimationUpdate方法,在這個方法裡,我們可以擷取目前的值和占用的比例我們可以計算出寬度是多少,比如時間過去了一半,目前值是50,比例是0.5,假設起始值為100,最終是500px,那麼500-100=400,所有這個時候乘以0.5=200,這些都是内部實作,我們不用自己寫,直接用。

7.3.5 屬性動畫的工作原理

繼續閱讀