Android高級動畫(1)

目錄

Android高級動畫(1)http://www.jianshu.com/p/48554844a2db
Android高級動畫(2)http://www.jianshu.com/p/89cfd9042b1e
Android高級動畫(3)http://www.jianshu.com/p/d6cc8d218900
Android高級動畫(4)http://www.jianshu.com/p/91f8363c3a8c

爲什麼要學好動畫

動畫在移動App開發中的重要性不言而喻,通俗點講,動畫可以讓我們的App界面不那麼死板,可以帶來酷炫的交互效果,用Material Design專業點的說法,動畫是一種高級的用戶反饋,對用戶操作、選擇結果的一種反饋,對程序執行過程的動態展示,對用戶視覺和注意力的引導,幫助用戶更好地理解App的功能設計,Android5.0新生的動畫更是體現出這個理念。所以從這點上說,動畫的意義遠不止酷炫這一層面。

那麼我們就更加有理由要學好動畫,把動畫應用到我們的項目中去。從這篇文章開始,我們將會全面地學習Android中的動畫系統。我們會講到每一種動畫的原理、使用方法、應用場景、優缺點等,關於矢量動畫,由於原理和實現方式相對複雜,我們會花比較大的篇幅來學習。最後,我們會封裝一個通用動畫庫來簡化動畫的使用。

OK,那我們廢話少說,開始一場愉快的Android動畫之旅吧!

大綱

4625917-165811b0d0dc545e.jpg
Android高級動畫課程大綱

Tween動畫

Tween動畫是Android最早的動畫框架,從Android1.0就開始提供。Tween動畫使用簡單,功能也不強,支持代碼和xml兩種方式編寫動畫。我們來看一個演示:

4625917-a3fccaca11064e8c.gif
Tween動畫

這是一個常規的Tween動畫,用xml編寫動畫,用AnimationUtils加載動畫,實現如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true">

    <rotate android:fromDegrees="0"
            android:toDegrees="359"
            android:duration="1000"
            android:repeatCount="1"
            android:repeatMode="reverse"
            android:pivotX="50%"
            android:pivotY="50%"/>
</set>
LinearLayout layout = (LinearLayout)findViewById(R.id.layout_1);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.tween_1);
layout.startAnimation(animation);

Tween動畫使用起來簡單,效果也比較流暢,但是缺點也是很明顯,首先就是拓展性太差,只能寫移動、縮放、旋轉、漸變四種動畫,以及這四種動畫的組合,不支持自定義View的拓展。其次一個致命的缺點就是動畫只是屏幕繪製上的動畫,控件的屬性並沒有改變,一個經典的問題就是一個Button從一個地方移動到另一個地方,點擊事件還是在原來的地方。所以Tween動畫我們現在也基本放棄了,所有使用Tween動畫的場景都可以用屬性動畫來替代。

下面屬性動畫閃亮登場

屬性動畫

屬性動畫是Android3.0版本推出的動畫框架,其功能和拓展性都很強。它不僅能實現所有Tween動畫的功能,還有很強的拓展性,根本原因是屬性動畫從本質上已經完全擺脫了控件,雖然我們大多數情況下使用屬性動畫都是給控件做動畫,但是屬性動畫的底層只是一個數值發生器,和控件沒有半毛錢關係。

[數值發生器]

ValueAnimator:數值發生器,這是屬性動畫的根本,ValueAnimator的功能就是在兩個數值範圍內,順序地產生過渡數值,過渡速率可以通過Intepolator來控制,過渡時間通過duration來設置,最終產生一組有特定變化速率的數值序列。那這個數值序列產生了幹嘛呢?你想幹嘛就幹嘛。

ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
animator.setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        float fraction = valueAnimator.getAnimatedFraction(); // 當前動畫進度百分比,範圍是0-1
        float value = (float)valueAnimator.getAnimatedValue(); // 當前動畫值,範圍是ofFloat的參數
    }
 });

[應用動畫]

通過ValueAnimator我們得到了一組變化的數值序列,大多數情況下我們都是拿來給控件做動畫,實現如下:

ValueAnimator animator = ValueAnimator.ofFloat(0, 360);
animator.setDuration(1000);
animator.setInterpolator(new AccelerateInterpolator());
animator.setRepeatCount(1);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        float value = (float)valueAnimator.getAnimatedValue();
        imageView.setRotationY(rotate);
    }
 });
animator.start();

我們使用ValueAnimator產生一組0-360的數值序列,數值變化速率遵循AccelerateInterpolator函數,時間爲1000毫秒,然後在回調函數裏我們可以獲取當前動畫值value,然後調用imageView.setRotationY(value);這樣就實現了imageView的旋轉動畫。效果如下:

4625917-14ebdfe45cfdef39.gif
ValueAnimator

[應用拓展]

上面產生的數值序列,我們除了可以用來做動畫,還可以用在任何你能想到的地方。比如我們有一個TextView顯示金額,這個金額要動畫地從0增加到2000,這就可以用ValueAnimator。

ValueAnimator animator = ValueAnimator.ofFloat(0, 2000);
animator.setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        float value = (float)valueAnimator.getAnimatedValue();
        textView.setText(String.valueOf(value));
    }
 });
animator.start();

效果如下:

4625917-697d9415120b2bca.gif
ValueAnimator

[ObjectAnimator]

可能你已經注意到了,ValueAnimator是屬性動畫底層的數值發生器,雖然拓展性很強,但是用它來寫動畫實在是麻煩,這一點Google當然也想到了,於是ObjectAnimator就誕生了,它把ValueAnimator封裝在裏面,我們用它可以簡單地實現對控件的動畫。代碼如下:

ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "rotationY", 0, 359);
animator.setDuration(1000);
animator.setRepeatCount(1);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.start();

效果和上面是一樣的

4625917-ac4b3ae7946d5118.gif
ObjectAnimator

第一行代碼有三個參數,分別是做動畫的控件, 需要動畫的屬性名,第三個是可變參數,表示動畫值的範圍。其中屬性名是一個字符串,ObjectAnimator會根據這個屬性名拼一個set[屬性名](setRotationY)方法,然後用反射調用,從而實現動畫。

[自定義View]

既然ValueAnimator可以用在任何地方,那當然也可以用在自定義View。我寫了一個很簡單的自定義View,就是畫一個扇形,扇形的角度由一個叫fraction的屬性決定。我們在自定View中需要做動畫的屬性一定要有一個setFraction方法,以便讓屬性動畫通過反射調用。

ObjectAnimator animator = ObjectAnimator.ofFloat(customView, "fraction", 0, 1);
animator.setDuration(2000);
animator.start();

效果如下:


4625917-afa7d31fd8ee1386.gif
CustomView

[Path動畫]

5.0之後,ObjectAnimator還有一個強大的功能,那就是根據一個Path路徑做動畫,先看效果:

4625917-ab5d16aca9cb8d32.gif
PathAnimator

這樣的效果要讓我們自己寫還是要費點事的,但是用ObjectAnimator來做就太簡單了:

ObjectAnimator animator = ObjectAnimator.ofFloat(iv, View.X, View.Y, path);
animator.setDuration(2000);
animator.setRepeatCount(1);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.start();

什麼?和正常的用法基本一樣?是的,區別就是ObjectAnimator.ofFloat方法的重載,傳四個參數,第一個是做動畫的控件,第二和第三個是動畫的方向,第四個就是動畫的Path路徑,就是這麼簡單,剩下的就是構建Path了,Path的構建你可以隨心所欲。你能構建出多酷炫的Path,它就能展示多酷炫的動畫。

Interpolator

Interpolator翻譯叫插值器,它是個好東西,它用來控制動畫過程的變化速率,沒有它我們的動畫將會一直勻速變化,如果要自己控制變化速率那也太麻煩了。Android中Interpolator總共有8種。

1、[Linear]

4625917-bd9b0c0493a1b0fc.png
Linear

2、[Accelerate]

4625917-5b0f5671a7ac161d.png
Accelerate

3、[Decelerate]

4625917-584dd9bc92e530f4.png
Decelerate

4、[AccelerateDecelerate]

4625917-bf0c5c001d7b8903.png
AccelerateDecelerate

5、[Anticipate]

4625917-44a7ef955bada1d2.png
Anticipate

6、[Overshoot]

4625917-abdde03c82aa9e8f.png
Overshoot

7、[Bounce]

4625917-09acf22e5248a503.png
Bounce

8、[Cycle]

4625917-37b10ea5ac323552.png
Cycle

幀動畫

幀動畫是一個比較簡單的動畫框架,原理就像播放幻燈片一樣,傳一組圖片進去,然後依次循環播放,可以設置每一張圖片的播放時間。幀動畫可以通過xml創建,也可以java代碼動態構建,使用起來還是比較方便的。先看效果:

4625917-d40c85c3684301fb.gif
FrameAnimator

代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
                android:oneshot="false">
    <item android:drawable="@mipmap/run_1" android:duration="150" />
    <item android:drawable="@mipmap/run_2" android:duration="150" />
    <item android:drawable="@mipmap/run_3" android:duration="150" />
    <item android:drawable="@mipmap/run_4" android:duration="150" />
</animation-list>
ImageView image = (ImageView) findViewById(R.id.iv_1);
image.setImageResource(R.drawable.animation_list);
AnimationDrawable anim = (AnimationDrawable) image.getDrawable();
anim.start();

可以看出來幀動畫的使用還是比較簡單的,但是缺點也很明顯,就是動畫不連續。如果要想動畫連續也簡單,就把圖片做的多一點,每張圖片只漸變一點點。但是這樣隨之而來的另一個問題就是圖片太多,體積太大。所以也只能在圖片流暢度和圖片數量體積之間做一個平衡。這點比較尷尬。但是一般來說用來做一個動畫的小Logo還是夠用的。

CircularReveral

這是Android5.0推出的新的動畫框架,可以給View做一個揭露效果。效果如下:

4625917-4603dc31410b3361.gif
CircularReveal

效果相當贊有木有!代碼實現也很簡單:

Animator anim = ViewAnimationUtils.createCircularReveal(oval, oval.getWidth() / 2, oval.getHeight() / 2, oval.getWidth(), 0);
anim.setDuration(2000);
anim.start();
Animator anim = ViewAnimationUtils.createCircularReveal(rect, 0, 0, 0, (float) Math.hypot(rect.getWidth(), rect.getHeight()));
animator2.setDuration(2000);
animator2.start();

五個參數分別是View,中心點座標,開始半徑,結束半徑。通過這五個參數的配合,我們可以做出很多不同的效果。

Activity轉場動畫

1、傳統轉場動畫

傳統轉場動畫我們再熟悉不過了,不再多說,直接看效果:

4625917-cbefaf7fee8059e2.gif
NormalTrans

代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="100%"
               android:toXDelta="0%"
               android:duration="500" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0%"
               android:toXDelta="-40%"
               android:duration="500" />
</set>
startActivity(new Intent(this, Activity2.class));
overridePendingTransition(R.anim.slide_right_in, R.anim.slide_left_out);

2、5.0新轉場動畫

這個厲害了!5.0新的轉場動畫分爲4種,Explode、Slide、Fade、Share,傳統的轉場動畫只能作用於整個頁面,不能對頁面中的單個元素做控制,而5.0新轉場動畫可以控制頁面中的每個元素,根據元素動畫方式,分爲4大類。
我們先來看下四種效果:

4625917-fe93e56d75147887.gif
NewTrans

(1)Explode
Explode的效果是下一個頁面的元素從四面八方進入,最終形成完整的頁面。代碼如下:

// 跳轉
Intent intent = new Intent(this, CActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

// 跳轉的Activity
public class CActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setEnterTransition(new Explode());
        setContentView(R.layout.activity_c);
    }
}

在跳轉時就要注意一點,intent後面還要再傳一個參數bundle,固定寫法ActivityOptions.makeSceneTransitionAnimation(this).toBundle(),下一個Activity根據這個就能識別出使用5.0新轉場動畫。

跳轉的Activity在onCreate方法中,調用getWindow().setEnterTransition(new Explode());即可,注意要在setContentView之前哦。

(2)Slide
Slide就是下一個頁面元素從底部依次向上運動,最終形成完整的頁面。代碼如下:

// 跳轉
Intent intent = new Intent(this, CActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

// 跳轉的Activity
public class CActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setEnterTransition(new Slide());
        setContentView(R.layout.activity_c);
    }
}

和Explode幾乎沒有什麼區別,就是跳轉的Activity在onCreate方法中設置transition爲Slide即可。

(3)Fade
Fade就是下一個頁面元素漸變出現,最終形成完整的頁面。代碼如下:

// 跳轉
Intent intent = new Intent(this, CActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

// 跳轉的Activity
public class CActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setEnterTransition(new Fade());
        getWindow().setExitTransition(new Fade());
        setContentView(R.layout.activity_c);
    }
}

同樣,就是跳轉的Activity在onCreate方法中設置transition爲Fade即可,只不過這裏要最好要同時設置Enter和Exit。

(4)Share
Share是最複雜的一種轉場方式,在跳轉的兩個Activity之間,如果有相同的View元素,那麼,兩個元素就可以設置成共享狀態,在跳轉時,這個View就會從第一個Activity的顯示狀態過渡到第二個Activity的顯示狀態,給用戶的感覺彷彿是兩個Activity共享一個View。

我們再來單獨看下Share的效果:

4625917-1a8a2a1f5fda254c.gif
Share

很酷炫有木有有木有

前後兩個Activty都有兩個共同的元素,一個機器人Logo,一個Android文字,但是它們在兩個Activity中的位置、大小、顏色都不一樣。於是,在兩個Activity跳轉過程中,位置、大小、顏色會自動有一個漸變的過程,從第一個Activity的狀態漸變到第二個Activity的狀態。從而給人一種兩個Activity共享元素的感覺。

代碼如下:

<!-- 首先,兩個Activity共享的元素需要設置相同的transitionName: android:transitionName="fab" -->
<Button
     android:id="@+id/fab_button"
     android:layout_width="56dp"
     android:layout_height="56dp"
     android:background="@mipmap/ic_launcher"
     android:elevation="5dp"
     android:onClick="explode"
     android:transitionName="fab" />

<Button
     android:id="@+id/fab_button"
     android:layout_width="160dp"
     android:layout_height="160dp"
     android:layout_alignParentEnd="true"
     android:layout_below="@id/holder_view"
     android:layout_marginTop="-80dp"
     android:background="@mipmap/ic_launcher"
     android:elevation="5dp"
     android:transitionName="fab" />
// 跳轉時,要爲每一個共享的view設置對應的transitionName
View fab = findViewById(R.id.fab_button);
View txName = findViewById(R.id.tx_user_name);
intent = new Intent(this, CActivity.class);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this,
        Pair.create(view, "share"),
        Pair.create(fab, "fab"),
        Pair.create(txName, "user_name"))
        .toBundle());
// 跳轉的Activity在onCreate方法中開啓Transition模式
public class CActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        setContentView(R.layout.activity_c);
    }
}

實現代碼看上去比較多,實際也比較簡單。畢竟這麼酷炫的轉場動畫化,多寫兩行代碼也值得。

(5)Share轉場的經典應用
Share轉場這種方式最經典的應用就是列表頁跳詳情頁。因爲通常列表頁和詳情頁都會包含相同View元素,使用Share轉場給人一種連續過渡效果,體驗非常棒。

下面看一個簡單的示例:

4625917-7243187d2282ff68.gif
demo

好了,Activity轉場就講到這裏,充分利用這些動畫,可以做出非常精彩的轉場效果。

下一篇

OK,Android高級動畫第一篇就到這裏了,爲什麼不講完呢?因爲下一篇我們要上點兒硬貨,來講講Android矢量動畫,徹底顛覆傳統的動畫方式。牛逼酷炫掉渣天的動畫效果正在等着你。

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