1. 程式人生 > >Transition:Activity過渡、共享元素動畫

Transition:Activity過渡、共享元素動畫

本文原專案地址為:
https://github.com/lgvalle/Material-Animations
在文章最後,有我自己對著這個專案手敲的一份。
程式碼基本一模一樣,只有略微的修改,加了一些註釋,以及將其中大多數英文翻譯成了中文。


此篇 API 均為 Android 5.0(API 級別 21) 以上才可支援。
此demo一共分為四部分:

1.1 普通過渡 Transition;
1.2 Shared Elements Transition 共享元素;
2.0 TransitionManager 控制動畫;
3.0 ViewAnimationUtils 顯示或隱藏效果。

過渡效果 Transition

Material Design 為應用中的切換頁面時,提供了非常優雅的視覺切換效果。
您可為進入、退出轉換、頁面之間的共享元素轉換設定特定的動畫。

1. Transition 動畫都包含哪些?

Android 5.0(API 級別 21)支援的進入與退出轉換有三個:

Explode Slide Fade
從中心移入或移出 從邊緣移入或移出 調整透明度產生漸變

一會看到使用場景的時候,就會發現上面的三張圖,每張圖都經歷了:(此處可以一會再回過頭來看)

退出 -> 進入  -> 返回   -> 重新進入
Exit -> Enter -> Return -> Reenter
 
**第一個頁面設定:**
android:windowExitTransition      啟動新 Activity ,此頁面退出的動畫
android:windowReenterTransition   重新進入的動畫。即第二次進入,可以和首次進入不一樣。
**第二個頁面設定:**
android:windowEnterTransition     首次進入顯示的動畫
android:windowReturnTransition    呼叫 finishAfterTransition() 退出時,此頁面退出的動畫
 
如此即可達到以上效果。

explode:從場景的中心移入或移出
slide:從場景的邊緣移入或移出
fade:調整透明度產生漸變效果

這三個類都繼承於 Transition ,所有有一些屬性都是共同的。
常用屬性如下:


// 設定動畫的時間。型別:long
transition.setDuration();
// 設定修飾動畫,定義動畫的變化率,具體設定往下翻就看到了
transition.setInterpolator();
// 設定動畫開始時間,延遲n毫秒播放。型別:long
transition.setStartDelay();
// 設定動畫的執行路徑
transition.setPathMotion();
// 改變動畫 出現/消失 的模式。Visibility.MODE_IN:進入;Visibility.MODE_OUT:退出。
transition.setMode();
 
// 設定動畫的監聽事件
transition.addListener()

至於例子,在下一個給出。
喏,這不就是了。

2. 修飾動畫,定義動畫的變化率(Interpolator)

在 Java 程式碼中定義:

Explode transition = new Explode();
transition.setDuration(500);
transition.setInterpolator(new AccelerateInterpolator());

在 Xml 資源定義:


<explode
    android:duration="@integer/anim_duration_long"
    android:interpolator="@android:interpolator/bounce"
    />

可選型別:


AccelerateDecelerateInterpolator 在動畫開始與結束的地方速率改變比較慢,在中間的時候加速
 
AccelerateInterpolator  在動畫開始的地方速率改變比較慢,然後開始加速
 
AnticipateInterpolator 開始的時候向後然後向前甩
 
AnticipateOvershootInterpolator 開始的時候向後然後向前甩一定值後返回最後的值
 
BounceInterpolator   動畫結束的時候彈起
 
CycleInterpolator 動畫迴圈播放特定的次數,速率改變沿著正弦曲線
 
DecelerateInterpolator 在動畫開始的地方快然後慢
 
LinearInterpolator   以常量速率改變
 
OvershootInterpolator    向前甩一定值後再回到原來位置

3. 設定 Transition 的時機

到底該什麼時候,設定什麼樣的過渡呢?

以下為動畫的設定場景:

首先開啟頁面A :
頁面A -> Enter 首次進入

從 A 開啟 B :
頁面A -> Exit 退出
頁面B -> Enter 首次進入

從 B 返回 A :
頁面B -> Return 返回
頁面A -> Reenter 重新進入

可設定的方法如下:

android:windowContentTransitions                允許使用transitions
 
android:windowAllowEnterTransitionOverlap        是否覆蓋執行,其實可以理解成前後兩個頁面是同步執行還是順序執行
 
android:windowAllowReturnTransitionOverlap        與上面相同。即上一個設定了退出動畫,這個設定了進入動畫,兩者是否同時執行。
 
android:windowContentTransitionManager            引用TransitionManager XML資源,定義不同視窗內容之間的所需轉換。
 
 
 
android:windowEnterTransition                    首次進入顯示的動畫
 
android:windowExitTransition                    啟動新 Activity ,此頁面退出的動畫
 
android:windowReenterTransition                    重新進入的動畫。即第二次進入,可以和首次進入不一樣。
 
android:windowReturnTransition                    呼叫 finishAfterTransition() 退出時,此頁面退出的動畫
 
 
 
android:windowSharedElementsUseOverlay            指示共享元素在轉換期間是否應使用疊加層。
 
android:windowSharedElementEnterTransition        首次進入顯示的動畫
 
android:windowSharedElementExitTransition        啟動新 Activity ,此頁面退出的動畫
 
android:windowSharedElementReenterTransition    重新進入的動畫。即第二次進入,可以和首次進入不一樣。
 
android:windowSharedElementReturnTransition        呼叫 finishAfterTransition() 退出時,此頁面退出的動畫

以上為 style 中設定屬性,在程式碼中設定為:

getWindow().setEnterTransition(visibility);
 // 其餘的都是類似

4. 跳轉頁面

至此,用以上知識基本可以設定出絕大多數的過渡效果。
然後,跳轉頁面跟普通的跳轉也有點不一樣。

跳轉頁面:


protected void transitionTo(Intent i) {
    ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(this);
    startActivity(i, transitionActivityOptions.toBundle());
}

退出頁面:

private void closeActivity(){
    // 如果定義了 return transition ,將使用 定義的動畫過渡
    Visibility returnTransition = buildReturnTransition();
    getWindow().setReturnTransition(returnTransition);
 
    // 如果沒有 return transition 被定義,將使用 反進入 的動畫
    finishAfterTransition();
}

注意:退出時,一定要呼叫:finishAfterTransition();



通過以上設定,就能夠完成一個基本的過渡效果了。


Shared Elements Transition 共享元素

效果如圖:

Shared Elements Transition

共享元素的各種設定 與 普通Transition 差不多。
來說說不同的地方。

攜帶需要共享的 View 進行跳轉

也就是在跳轉的引數中,增加了要共享的 View控制元件。
程式碼如下:

 
protected void transitionTo(Intent i) {
    ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
                                                    Pair.create(view1, "agreedName1"),
                                                    Pair.create(view2, "agreedName2"));
    startActivity(i, options.toBundle());
}

增加的引數的介紹:

Pair.create(
   View view,      // 本頁面要共享的 View
   String resId    // 下一個頁面的 View 的 id,注意是 id 的字串
)

​​​​​​​頁面切換時,動畫的效果設定

Android 5.0(API 級別 21)支援轉換效果如下:

changeBounds - 改變目標檢視的佈局邊界
changeClipBounds - 裁剪目標檢視邊界
changeTransform - 改變目標檢視的縮放比例和旋轉角度
changeImageTransform - 改變目標圖片的大小和縮放比例

設定程式碼:

Slide slide = new Slide();
slide.setDuration(500);
 
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(500);
 
getWindow().setEnterTransition(slide);
getWindow().setSharedElementEnterTransition(changeBounds);


Shared Elements Transition 就是 特殊的 Transition 用法,都是一樣的。


TransitionManager 控制動畫

這個框架可以讓一些複雜的動畫特別簡單的被實現。

TransitionManager

簡單的說明一下步驟:

  1. 定義需要切換 layout xml頁面;
  2. 呼叫 Scene.getSceneForLayout() 儲存每個Layout;
  3. 呼叫 TransitionManager.go(scene1, new ChangeBounds()) 切換。

相當於定義了不同的 xml 佈局,然後通過簡單的呼叫,就完成了較為複雜的動畫。

以下為程式碼片段:

private void setupLayout() {
    scene0 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene0, this);
    scene1 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene1, this);
    scene2 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene2, this);
    scene3 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene3, this);
    scene4 = Scene.getSceneForLayout(binding.sceneRoot, R.layout.activity_animations_scene4, this);
    binding.sample3Button1.setOnClickListener(this);
    binding.sample3Button2.setOnClickListener(this);
    binding.sample3Button3.setOnClickListener(this);
    binding.sample3Button4.setOnClickListener(this);
}
 
@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.sample3_button1:
            TransitionManager.go(scene1, new ChangeBounds());
            break;
        case R.id.sample3_button2:
            TransitionManager.go(scene2, TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds));
            break;
        case R.id.sample3_button3:
            TransitionManager.go(scene3,TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential));
            break;
        case R.id.sample3_button4:
            TransitionManager.go(scene3,TransitionInflater.from(this).inflateTransition(R.transition.slide_and_changebounds_sequential_with_interpolators));
            break;
    }
}

只需要定義佈局,呼叫調轉,
就實現了,點選四個 Button ,分別切換四個佈局。
TransitionManager.go() 中可以設定各種動畫。
 


CircularReveal 顯示或隱藏 的效果

ViewAnimationUtils.createCircularReveal()
當您顯示或隱藏一組 UI 元素時,Circular Reveal 可為使用者提供視覺連續性

CircularReveal

引數說明:

Animator createCircularReveal (View view, // 將要變化的 View
            int centerX,                  // 動畫圓的中心的x座標
            int centerY,                  // 動畫圓的中心的y座標
            float startRadius,            // 動畫圓的起始半徑
            float endRadius               // 動畫圓的結束半徑
)

顯示 View :

private void animShow() {
    View myView = findViewById(R.id.my_view);
    // 從 View 的中心開始
    int cx = (myView.getLeft() + myView.getRight()) / 2;
    int cy = (myView.getTop() + myView.getBottom()) / 2;
    int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
 
    //為此檢視建立動畫設計(起始半徑為零)
    Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
    // 使檢視可見並啟動動畫
    myView.setVisibility(View.VISIBLE);
    anim.start();
}

隱藏 View :

private void animHide() {
    final View myView = findViewById(R.id.my_view);
    int cx = (myView.getLeft() + myView.getRight()) / 2;
    int cy = (myView.getTop() + myView.getBottom()) / 2;
 
    int initialRadius = myView.getWidth();
 
    // 半徑 從 viewWidth -> 0
    Animator anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
 
    anim.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            myView.setVisibility(View.INVISIBLE);
        }
    });
    anim.start();
}

因為,這些炫酷的效果都只支援 API 23 以上,所以在我們常用的 APP 中都還不常見。但是效果真的很不錯。

值得大家研究一下。
此文是我的一個總結。

專案地址:
https://github.com/Wing-Li/Material-Animations-CN

這個專案是我對著原專案手敲的,
基本一模一樣,只是加了一些註釋,以及將其中英文翻譯成了中文。
大家可以參考參考。


 

文/Wing_Li(簡書作者)
原文連結:http://www.jianshu.com/p/a43daa1e3d6e
著作權歸作者所有,轉載請聯絡作者獲得授權,並標註“簡書作者”。