小玩一把__MaterialDesign過渡動畫

這篇文章的套路如下薄坏,帶你裝逼帶你飛。

  1. 概念
  2. 如何使用
    • 設(shè)置 相同的名稱
    • 啟動 startActivity
    • Fragment to Fragment
    • 設(shè)置多組元素
  3. 自定義過度動畫
  4. 源碼解讀
  5. 如何兼容低版本

先看一下 效果吧棍厂!


系統(tǒng)自帶過渡動畫

自定義過渡動畫

1.概念

在5.0之前侧馅,不同活動或片段的過渡是進(jìn)入和退出動畫,視圖層次結(jié)構(gòu)彼此獨立的轉(zhuǎn)換源葫。
過渡動畫則是:把兩個activity當(dāng)中的相同元素關(guān)聯(lián)起來做連貫的動畫; 從而達(dá)到不同視圖之間的元素關(guān)聯(lián)達(dá)到 酷炫雄坪、爆炸等酷炫而又優(yōu)雅地效果。
引導(dǎo)用戶視覺匹厘。

共享元素類型:

explode:從場景的中心移入或移出
slide:從場景的邊緣移入或移出
fade:調(diào)整透明度產(chǎn)生漸變效果

動畫效果:

changeBounds - 改變目標(biāo)視圖的布局邊界
changeClipBounds - 裁剪目標(biāo)視圖邊界
changeTransform - 改變目標(biāo)視圖的縮放比例和旋轉(zhuǎn)角度
changeImageTransform - 改變目標(biāo)圖片的大小和縮放比例

2. 如何使用

2.1 設(shè)置主題

先要在系統(tǒng)主題中設(shè)置主題嘀趟,或者在java代碼中設(shè)置支持過渡動畫的主題,我們在頁面切換時才有過渡動畫的效果愈诚,有以下兩種方式:

2.1.1 方法一

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);  

// 設(shè)置一個exit transition
getWindow().setExitTransition(new Explode());

可以通過如下方法在代碼總設(shè)置進(jìn)入與退出時 Transition 效果:

  • Window.setEnterTransition():普通transition的進(jìn)入效果
  • Window.setExitTransition():普通transition的退出效果
  • Window.setSharedElementEnterTransition():共享元素transition的進(jìn)入效果
  • Window.setSharedElementExitTransition():共享元素transition的退出效果

2.1.2 方法二

在xml中設(shè)置 默認(rèn)配置

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="android:windowContentTransitions">true</item>
    
        <!-- 指定進(jìn)入和退出transitions -->
    <item name="android:windowEnterTransition">@transition/explode</item>
    <item name="android:windowExitTransition">@transition/explode</item>
    
        <!-- 指定shared element transitions -->
    <item name="android:windowSharedElementEnterTransition">
        @transition/change_image_transform</item>
    <item name="android:windowSharedElementExitTransition">
        @transition/change_image_transform</item>
    ...
</style>

2.2 設(shè)置 相同的名稱

共享元素切換她按,首先要保證兩個頁面內(nèi)view的transitionName是一致的
設(shè)置:android:transitionName
如下:
MainActivity.xml

<android.support.v7.widget.CardView
  ...>
      <ImageView
          android:id="@+id/ivProfile"
          android:transitionName="profile"
          android:scaleType="centerCrop"
          android:layout_width="match_parent"
          android:layout_height="160dp" />
      ...
</android.support.v7.widget.CardView>

DetailActivity.xml

<LinearLayout
  ...>
      <ImageView
          android:id="@+id/ivProfile"
          android:transitionName="profile"
          android:scaleType="centerCrop"
          android:layout_width="match_parent"
          android:layout_height="380dp" />
      ...
</LinearLayout>

2.3 啟動 startActivity

第一個Acitivty執(zhí)行頁面跳轉(zhuǎn)時需要加上 ActivityOptionsCompat.toBundle() 才會顯示出過渡動畫的效果

Intent intent = new Intent(this, DetailsActivity.class);
// Pass data object in the bundle and populate details activity.
intent.putExtra(DetailsActivity.EXTRA_CONTACT, contact);

//
getWindow().setExitTransition(new Explode());  //設(shè)置頁面切換效果
//第一次進(jìn)入時使用
getWindow().setEnterTransition(new explode);
//再次進(jìn)入時使用
getWindow().setReenterTransition(new explode);

Transition ts = new ChangeClipBounds();         //設(shè)置元素動畫
ts.setDuration(3000);
getWindow().setSharedElementExitTransition(ts);

ActivityOptionsCompat options = ActivityOptionsCompat.
    makeSceneTransitionAnimation(this, (View)ivProfile, "profile");    
startActivity(intent, options.toBundle());

從第二個Activity反轉(zhuǎn)場景轉(zhuǎn)換動畫勋桶,請調(diào)用Activity.supportFinishAfterTransition()而不是Activity.finish()汁尺,如果你使用了toolbar的返回按鈕行為疟呐,也需要去覆蓋它

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        // Respond to the action bar's Up/Home button
        case android.R.id.home:
            supportFinishAfterTransition();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

2.4 Fragment to Freagment

// Get access to or create instances to each fragment
FirstFragment fragmentOne = ...;
SecondFragment fragmentTwo = ...;
// Check that the device is running lollipop
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Inflate transitions to apply
    Transition changeTransform = TransitionInflater.from(this).
          inflateTransition(R.transition.change_image_transform);
    Transition explodeTransform = TransitionInflater.from(this).
          inflateTransition(android.R.transition.explode);
 
    // Setup exit transition on first fragment
    fragmentOne.setSharedElementReturnTransition(changeTransform);
    fragmentOne.setExitTransition(explodeTransform);

    // Setup enter transition on second fragment
    fragmentTwo.setSharedElementEnterTransition(changeTransform);
    fragmentTwo.setEnterTransition(explodeTransform);

    // Find the shared element (in Fragment A)
    ImageView ivProfile = (ImageView) findViewById(R.id.ivProfile);

    // Add second fragment by replacing first 
    FragmentTransaction ft = getFragmentManager().beginTransaction()
            .replace(R.id.container, fragmentTwo)
            .addToBackStack("transaction")
            .addSharedElement(ivProfile, "profile");
    // Apply the transaction
    ft.commit();
}
else {
    // Code to run on older devices
}

2.6 設(shè)置多組元素

如最開始的展示效果豆挽,可以知道,我們是可以設(shè)置多組元素的關(guān)聯(lián)并且對每個元素可以執(zhí)行不同的過渡動畫的挖胃,它的方式如下:

Intent intent = new Intent(context, DetailsActivity.class);
intent.putExtra(DetailsActivity.EXTRA_CONTACT, contact);
Pair<View, String> p1 = Pair.create((View)ivProfile, "profile");
Pair<View, String> p2 = Pair.create(vPalette, "palette");
Pair<View, String> p3 = Pair.create((View)tvName, "text");
ActivityOptionsCompat options = ActivityOptionsCompat.
    makeSceneTransitionAnimation(this, p1, p2, p3);
startActivity(intent, options.toBundle());

注意:默認(rèn)情況下android.util.Pair將被導(dǎo)入啃洋,但是我們要選擇android.support.v4.util.Pair類碾盐。

這里要避免元素過多導(dǎo)致分散注意力的動畫欢嘿,這樣可能會失去過渡動畫的意義衰琐。

3.自定義過度動畫

本質(zhì)上就是重寫 Transition;

套路如下:
1炼蹦、繼承 Visibility 或者 Transition
2羡宙、自定義動畫:實現(xiàn) VIsibility/Transition內(nèi)方法
3、在頁面中將自定義Transition設(shè)入: getWindow().setEnterTransition或者setSharedElementEnterTransition

看一下自定義的效果

自定義過渡動畫

3.1 一些概念:

  1. Visibility extends Transition 頁面切換轉(zhuǎn)場動畫類掐隐;
  2. shareView 的移動的軌跡路徑PathMotion類
ArcMotion arcMotion = new ArcMotion();
arcMotion.setMinimumHorizontalAngle(50f);
arcMotion.setMinimumVerticalAngle(50f);

例子:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/transition/ChangePosition.java

3.2 自定義動畫

繼承 Visibility:

public void captureStartValues(TransitionValues transitionValues) 這里保存計算動畫初始狀態(tài)的一個屬性值
public void captureEndValues(TransitionValues transitionValues) 這里保存計算動畫結(jié)束狀態(tài)的一個屬性值 

public Animator onAppear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)
如果是進(jìn)入動畫 即顯示某個 View 則會執(zhí)行這個方法  
public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues, TransitionValues endValues)
如果是退出 ,  VIew 則會執(zhí)行這個方法

例子:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/transition/CommentEnterTransition.java

繼承 Transition:

    
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        //初始值保存
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) { 
    //結(jié)束值保存
    }
    
    createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues){
  //創(chuàng)建執(zhí)行動畫
  }

例子:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/transition/ChangeColor.java

3.3 調(diào)用

            getWindow().setEnterTransition(new CommentEnterTransition(this, toolbar, bottom_aty_love));//設(shè)置
            getWindow().setSharedElementEnterTransition(buildShareElemEnterSet());//設(shè)置進(jìn)入轉(zhuǎn)換動畫
            getWindow().setSharedElementReturnTransition(buildShareElemReturnSet());//設(shè)置退出轉(zhuǎn)換動畫

自定義過渡動畫的Activity:https://github.com/LidongWen/LittlePrincess/blob/master/app/src/main/java/com/wenld/littleprincess/activity/LoveActivity.java

4. 源碼解讀

帶著幾個疑問:

  • Transition內(nèi)方法在哪里被調(diào)用
  • 看一個 系統(tǒng) 的 轉(zhuǎn)換類寫法 狗热,這邊選擇Explode
  1. TransitionKitKat 內(nèi)被調(diào)用
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        android.transition.TransitionValues internalValues =
                new android.transition.TransitionValues();
        copyValues(transitionValues, internalValues);
        mTransition.captureEndValues(internalValues);
        copyValues(internalValues, transitionValues);
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        android.transition.TransitionValues internalValues =
                new android.transition.TransitionValues();
        copyValues(transitionValues, internalValues);
        mTransition.captureStartValues(internalValues);
        copyValues(internalValues, transitionValues);
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
            TransitionValues endValues) {
        android.transition.TransitionValues internalStartValues;
        android.transition.TransitionValues internalEndValues;
        if (startValues != null) {
            internalStartValues = new android.transition.TransitionValues();
            copyValues(startValues, internalStartValues);
        } else {
            internalStartValues = null;
        }
        if (endValues != null) {
            internalEndValues = new android.transition.TransitionValues();
            copyValues(endValues, internalEndValues);
        } else {
            internalEndValues = null;
        }
        return mTransition.createAnimator(sceneRoot, internalStartValues, internalEndValues);
    }

Explode源碼

  1. Explode結(jié)構(gòu)如下:
public class Explode extends Visibility {
    public Explode() {
        setPropagation(new CircularPropagation());
    }
    private void captureValues(TransitionValues transitionValues) {
    }

//初始值
    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        super.captureStartValues(transitionValues);
        captureValues(transitionValues);
    }
//結(jié)束值
    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        super.captureEndValues(transitionValues);
        captureValues(transitionValues);
    }
// 進(jìn)入時被調(diào)用
    @Override
    public Animator onAppear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {
            //執(zhí)行動畫
        return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
                startX, startY, endX, endY, sDecelerate, this);
    }
//退出頁面時調(diào)用
    @Override
    public Animator onDisappear(ViewGroup sceneRoot, View view,
            TransitionValues startValues, TransitionValues endValues) {

        return TranslationAnimationCreator.createAnimation(view, startValues,
                viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate, this);
    }

    private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
    }
}

太簡單了,虑省,就是調(diào)用 TranslationAnimationCreator.createAnimation 創(chuàng)建動畫....,我們上面的自定義過渡動畫就是跟 這貨學(xué)習(xí)到....

5. 兼容低版本

到了這里匿刮, 送大家一首歌:感覺身體被掏空.......
由于復(fù)雜度的原因,
低版本兼容還是說下思路吧探颈,以后有時間或者有明確需求在去實現(xiàn):
思路:

  1. 將第一個頁面中共享元素拿出保存熟丸。
  2. 在第二個頁面的onCreate()方法內(nèi),設(shè)置動畫膝擂,并執(zhí)行第一個頁面view的動畫虑啤。
  3. 第二個界面退出時,執(zhí)行第二個界面共享元素動畫架馋。

大玩一發(fā)——MaterialDesign元素轉(zhuǎn)換動畫兼容低版本(有可能不會寫D健)
網(wǎng)絡(luò)上有人實現(xiàn)過 大概是這種思路:https://github.com/huzenan/EasyTransition,具體代碼我沒運行過叉寂,不過目測應(yīng)該是可行的萍启。

參考:

demo地址:戳我!!!


希望我的文章不會誤導(dǎo)在觀看的你,如果有異議的地方歡迎討論和指正屏鳍。
如果能給觀看的你帶來收獲勘纯,那就是最好不過了。

人生得意須盡歡, 桃花塢里桃花庵
點個關(guān)注唄钓瞭,對驳遵,不信你點試試?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末山涡,一起剝皮案震驚了整個濱河市堤结,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸭丛,老刑警劉巖竞穷,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鳞溉,居然都是意外死亡瘾带,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門熟菲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來看政,“玉大人,你說我怎么就攤上這事抄罕∶毖茫” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵贞绵,是天一觀的道長厉萝。 經(jīng)常有香客問我,道長榨崩,這世上最難降的妖魔是什么谴垫? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮母蛛,結(jié)果婚禮上翩剪,老公的妹妹穿的比我還像新娘。我一直安慰自己彩郊,他們只是感情好前弯,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布蚪缀。 她就那樣靜靜地躺著,像睡著了一般恕出。 火紅的嫁衣襯著肌膚如雪询枚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天浙巫,我揣著相機(jī)與錄音金蜀,去河邊找鬼。 笑死的畴,一個胖子當(dāng)著我的面吹牛渊抄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丧裁,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼护桦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了煎娇?” 一聲冷哼從身側(cè)響起嘶炭,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逊桦,沒想到半個月后眨猎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡强经,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年睡陪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匿情。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡兰迫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出炬称,到底是詐尸還是另有隱情汁果,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布玲躯,位于F島的核電站据德,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏跷车。R本人自食惡果不足惜棘利,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望朽缴。 院中可真熱鬧善玫,春花似錦、人聲如沸密强。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至系冗,卻和暖如春奕扣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毕谴。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留距芬,地道東北人涝开。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像框仔,于是被迫代替她去往敵國和親舀武。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354

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