Android高級動畫(1)

目錄

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

為什么要學(xué)好動畫

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

那么我們就更加有理由要學(xué)好動畫,把動畫應(yīng)用到我們的項目中去。從這篇文章開始,我們將會全面地學(xué)習(xí)Android中的動畫系統(tǒng)。我們會講到每一種動畫的原理、使用方法、應(yīng)用場景、優(yōu)缺點等,關(guān)于矢量動畫,由于原理和實現(xiàn)方式相對復(fù)雜,我們會花比較大的篇幅來學(xué)習(xí)。最后,我們會封裝一個通用動畫庫來簡化動畫的使用。

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

大綱

Android高級動畫課程大綱

Tween動畫

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

Tween動畫

這是一個常規(guī)的Tween動畫,用xml編寫動畫,用AnimationUtils加載動畫,實現(xiàn)如下:

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

下面屬性動畫閃亮登場

屬性動畫

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

[數(shù)值發(fā)生器]

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

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

[應(yīng)用動畫]

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

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產(chǎn)生一組0-360的數(shù)值序列,數(shù)值變化速率遵循AccelerateInterpolator函數(shù),時間為1000毫秒,然后在回調(diào)函數(shù)里我們可以獲取當(dāng)前動畫值value,然后調(diào)用imageView.setRotationY(value);這樣就實現(xiàn)了imageView的旋轉(zhuǎn)動畫。效果如下:

ValueAnimator

[應(yīng)用拓展]

上面產(chǎn)生的數(shù)值序列,我們除了可以用來做動畫,還可以用在任何你能想到的地方。比如我們有一個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();

效果如下:

ValueAnimator

[ObjectAnimator]

可能你已經(jīng)注意到了,ValueAnimator是屬性動畫底層的數(shù)值發(fā)生器,雖然拓展性很強(qiáng),但是用它來寫動畫實在是麻煩,這一點Google當(dāng)然也想到了,于是ObjectAnimator就誕生了,它把ValueAnimator封裝在里面,我們用它可以簡單地實現(xiàn)對控件的動畫。代碼如下:

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

效果和上面是一樣的

ObjectAnimator

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

[自定義View]

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

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

效果如下:


CustomView

[Path動畫]

5.0之后,ObjectAnimator還有一個強(qiáng)大的功能,那就是根據(jù)一個Path路徑做動畫,先看效果:

PathAnimator

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

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

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

Interpolator

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

1、[Linear]

Linear

2、[Accelerate]

Accelerate

3、[Decelerate]

Decelerate

4、[AccelerateDecelerate]

AccelerateDecelerate

5、[Anticipate]

Anticipate

6、[Overshoot]

Overshoot

7、[Bounce]

Bounce

8、[Cycle]

Cycle

幀動畫

幀動畫是一個比較簡單的動畫框架,原理就像播放幻燈片一樣,傳一組圖片進(jìn)去,然后依次循環(huán)播放,可以設(shè)置每一張圖片的播放時間。幀動畫可以通過xml創(chuàng)建,也可以java代碼動態(tài)構(gòu)建,使用起來還是比較方便的。先看效果:

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();

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

CircularReveral

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

CircularReveal

效果相當(dāng)贊有木有!代碼實現(xiàn)也很簡單:

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();

五個參數(shù)分別是View,中心點坐標(biāo),開始半徑,結(jié)束半徑。通過這五個參數(shù)的配合,我們可以做出很多不同的效果。

Activity轉(zhuǎn)場動畫

1、傳統(tǒng)轉(zhuǎn)場動畫

傳統(tǒng)轉(zhuǎn)場動畫我們再熟悉不過了,不再多說,直接看效果:

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新轉(zhuǎn)場動畫

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

NewTrans

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

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

// 跳轉(zhuǎn)的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);
    }
}

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

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

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

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

// 跳轉(zhuǎn)的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幾乎沒有什么區(qū)別,就是跳轉(zhuǎn)的Activity在onCreate方法中設(shè)置transition為Slide即可。

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

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

// 跳轉(zhuǎn)的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);
    }
}

同樣,就是跳轉(zhuǎn)的Activity在onCreate方法中設(shè)置transition為Fade即可,只不過這里要最好要同時設(shè)置Enter和Exit。

(4)Share
Share是最復(fù)雜的一種轉(zhuǎn)場方式,在跳轉(zhuǎn)的兩個Activity之間,如果有相同的View元素,那么,兩個元素就可以設(shè)置成共享狀態(tài),在跳轉(zhuǎn)時,這個View就會從第一個Activity的顯示狀態(tài)過渡到第二個Activity的顯示狀態(tài),給用戶的感覺仿佛是兩個Activity共享一個View。

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

Share

很酷炫有木有有木有

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

代碼如下:

<!-- 首先,兩個Activity共享的元素需要設(shè)置相同的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" />
// 跳轉(zhuǎn)時,要為每一個共享的view設(shè)置對應(yīng)的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());
// 跳轉(zhuǎn)的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);
    }
}

實現(xiàn)代碼看上去比較多,實際也比較簡單。畢竟這么酷炫的轉(zhuǎn)場動畫化,多寫兩行代碼也值得。

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

下面看一個簡單的示例:

demo

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

下一篇

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內(nèi)容