Android 轉場動畫

劉強東發表於2019-03-04

轉場動畫

轉場動畫: 是Android L 引入的動畫效果, 可以說是api19引入的場景(Scene)動畫的擴充套件. 使開發者更加方便的實現佈局(介面)變化時候的過渡動畫.

Android L 是Google於2014年升級的系統版本號, 在2015年國內廠商新機就開始推送Android L, 現在是2017年我覺得此時不用更待何時.

轉場動畫(Transition)

在android.transition包下提供關於transitionAnimation的過渡框架, Transiton框架是在api19引入, 但是轉場動畫卻是在api21引入.

Tip: 某些動畫效果可能需要api23之上

類關係:

  • Transition
    • ChangeBounds 改變目標檢視的佈局邊界
    • ChangeClipBounds 裁剪目標檢視邊界
    • ChangeTransform 改變目標檢視的縮放比例和旋轉角度
    • ChangeImageTransform 改變目標圖片的大小和縮放比例
    • ChangeScroll
    • TransitionSet
      • AutoTransition 預設過渡動畫
    • Visibility 其子類都屬於介面切換動畫
      • Explode 爆炸
      • Fade 淡出
      • Slide 上下滑動

Transition是一個不需要考慮關鍵幀(keyFrame)只需要告訴系統你想要的動畫效果就能實現其過渡

帶有change字首的Transition的子類只是針對特有屬性才有效果

而Visibility的子類是針對共享元素的座標建立動畫效果

簡單的效果實現演示:

介面A

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.button) Button mButton;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.button) public void onClick() {
        startActivity(new Intent(this, SecondActivity.class), ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());
    }
}
複製程式碼

介面B

public class SecondActivity extends AppCompatActivity {

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); // 必須
        setContentView(R.layout.activity_second);

        getWindow().setExitTransition(new Slide());
        getWindow().setEnterTransition(new Slide());
    }
}
複製程式碼

Tip: 官方文件說明轉場動畫要求當前Activity必須在setContentView之前寫入如下程式碼, 不過api21以上並不需要以下設定.

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
複製程式碼

如果要退出介面仍然有transition動畫不能執行finish, 需要執行finishAfterTransition()

動畫目標

預設轉場動畫會對所有的子View進行遍歷載入動畫, 但是如果新增目標則不會進行遍歷所有子View, 或者你也可以排除特定View.

對於目標有三個操作

  • 新增

    預設會進行遍歷所有的檢視載入動畫, 但是如果使用了新增就不會遍歷所有, 只會讓指定的檢視進行動畫

  • 排除

    如果使用排除方法, 依舊會進行遍歷檢視物件, 不過會排除你指定的檢視

  • 刪除

    刪除目標是在動畫已經遍歷檢視完成以後還想對目標集合進行變更, 就可以刪除指定的檢視

新增/排除和刪除目標支援以下引數型別

  1. 檢視物件(View)
  2. 過渡名(TransitionNames)
  3. 位元組碼(Class)
  4. ID
Transition addTarget (View target)

Transition addTarget (String targetName)

Transition addTarget (Class targetType)

Transition addTarget (int targetId)
複製程式碼

刪除是removeTarget(), 排除是excludeTarget()

轉場動畫都支援設定監聽器

Transition addListener (Transition.TransitionListener listener)
複製程式碼

TransitionListener和AnimatorListener一樣的重寫方法.

視窗(Window)

從示例可以看出轉場動畫在程式碼中是通過獲取Window物件進行設定的. 看下Window有哪些關於轉場動畫的方法.

轉場動畫有四種場景:

// 當前介面進入動畫
void setEnterTransition (Transition transition)
// 當前介面退出動畫
void setExitTransition (Transition transition)
  
// 以下介紹的是返回時的動畫, 如果不設定就會預設和進入和退出動畫相同
  
// 下個介面返回當前介面時, 當前介面進入動畫
void setReenterTransition (Transition transition)
  
// 返回上個介面時當前介面退出動畫
void setReturnTransition (Transition transition)
複製程式碼

預設情況下介面A的退出動畫還沒有結束時, 介面B的進入動畫就會開始執行. 以下兩個方法預設為true. 想要進入動畫等待退出動畫結束後再播放就需要以下兩個方法設定為false.

建議處於預設開啟的狀態, 否則可能出現背景空白期. 如果想清晰的看出幾個不同狀態的動畫順序可以開啟

void setAllowEnterTransitionOverlap (boolean allow)
void setAllowReturnTransitionOverlap (boolean allow)
複製程式碼

直接設定一個

void setTransitionManager (TransitionManager tm)
複製程式碼

在分享元素過渡的時候是否允許重疊

void setSharedElementsUseOverlay (boolean sharedElementsUseOverlay)
複製程式碼

Tip: 轉場動畫還支援主題檔案裡面直接設定

介面選項(ActivityOptions)

如果想相容api16之前的系統版本可以使用ActivityOptionsCompat

該類用跳轉介面的使用作為可選引數傳遞;

如果想讓轉場動畫生效就必須使用下面兩種方法開啟介面

// 單一共享元素, 如果沒有共享元素傳入NULL
ActivityOptions makeSceneTransitionAnimation (Activity activity, 
                View sharedElement, 
                String sharedElementName)

// 支援多個共享元素
ActivityOptions makeSceneTransitionAnimation (Activity activity, 
                Pair...<View, String> sharedElements)
複製程式碼

自定義進入和退出動畫, 和overridePendingTransition方法一樣

ActivityOptions makeCustomAnimation (Context context, 
                int enterResId, 
                int exitResId)
複製程式碼

下面介紹三種系統提供的預設動畫效果, 我實際使用感覺效果不是很明顯, 感覺用處不大.

裁剪動畫, 這是api23(Android m) 新增api.

ActivityOptions makeClipRevealAnimation (View source, 
                int startX, 
                int startY, 
                int width, 
                int height)
複製程式碼

縮放動畫

ActivityOptions makeScaleUpAnimation (View source, 
                int startX, 
                int startY, 
                int width, 
                int height)
複製程式碼

縮圖

ActivityOptions makeThumbnailScaleUpAnimation (View source, 
                Bitmap thumbnail, 
                int startX, 
                int startY)
複製程式碼

場景(Scene)

場景可以理解為在一個介面(Activity)當中切換或者改變佈局內容, 場景是在api19引入

建立方式有兩種, 一種是通過以下的靜態方法直接建立

Scene getSceneForLayout (ViewGroup sceneRoot, 
                int layoutId, 
                Context context)
複製程式碼

或者通過構造方法

Scene (ViewGroup sceneRoot,  // 當前場景
                View layout) // 需要進入的新場景
複製程式碼

sceneRoot可以稱為根檢視. 在官方示例中是當前介面佈局的檢視物件, 可以理解為其場景(Scene)依附的Activity容器

如果你需要動態的設定場景的檢視內容, 可以只指定根檢視

Scene (ViewGroup sceneRoot)
複製程式碼

進入場景和退出場景, 不帶任何動畫. 一般情況下場景的進入由TransitionManager負責

void enter ()

void exit ()
複製程式碼

exit並不會對場景有任何變化

在場景進入和退出時可以設定兩個回撥方法

void setEnterAction (Runnable action)

void setExitAction (Runnable action)
複製程式碼

Tip: 如果兩個場景存在相同的ID會自動進行共享元素動畫(前提是用的不是預設的AutoTransiton, 因為該類沒有使用Change)

過渡管理器(TransitionManager)

Scene預設是沒有針對場景的變化, TransitionManager提供兩種方式

  1. 這種預設的過渡動畫是 AutoTransition

    static void go (Scene scene)
    複製程式碼

  2. 自定義過渡動畫

    static void go (Scene scene, 
                    Transition transition)
    複製程式碼

開始延遲過渡, 即儲存當前檢視的屬性狀態, 然後在之後其發生改變的時候自動進行過渡動畫

void beginDelayedTransition (ViewGroup sceneRoot)

void beginDelayedTransition (ViewGroup sceneRoot, 
                Transition transition)
複製程式碼

後面還可以結束自動儲存屬性狀態

void endTransitions (ViewGroup sceneRoot)
複製程式碼

場景切換

// 建立一個自定義Transition動畫的TransitionManager
mTransitionManagerForScene3 = TransitionInflater.from(getActivity())
.inflateTransitionManager(R.transition.scene3_transition_manager, mSceneRoot);

// 然後切換任意佈局
mTransitionManagerForScene3.transitionTo(mScene3);
複製程式碼

該方法等同於提到的go()

void transitionTo (Scene scene)
複製程式碼

XML定義

不同於普通的動畫, 轉場動畫擁有專屬的資源目錄transition

在res/transition目錄下建立XML檔案

Android 轉場動畫

標籤一覽

Android 轉場動畫

引用XML

  Transition explode = TransitionInflater.from(this).inflateTransition(R.transition.explode);
複製程式碼

或者可以直接在主題中設定

<item name="android:windowExitTransition">@transition/explode</item>
<item name="android:windowEnterAnimation">@transition/explode</item>
<item name="android:windowReenterTransition">@transition/explode</item>
複製程式碼

TransitionInflat除上面提到的方法外還可以填充trantionManager物件

TransitionManager inflateTransitionManager (int resource, 
                ViewGroup sceneRoot)
複製程式碼

共享元素(ShareElement)

共享元素

一般是在關聯的介面之前存在相同的(或者說類似的, 並不強制一定要相同)控制元件元素就會使用, 例如下圖的按鈕:

Android 轉場動畫

A介面跳轉要指定共享元素

        startActivity(new Intent(this, SecondActivity.class),
                ActivityOptionsCompat.makeSceneTransitionAnimation(this, mButton, "button")
                        .toBundle());
複製程式碼

B介面的佈局檔案中指定共享元素android:transitionName="button"

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第二個介面"
        android:transitionName="button"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="99dp"
        />
複製程式碼

可以看到所謂的共享元素即在兩個介面的兩個控制元件(或多個控制元件)之間的過渡變化效果

可以看到轉場動畫的操作都是針對Window物件, 擁有如下方法(同時都擁有對應的getter方法)

共享元素

void setSharedElementEnterTransition (Transition transition)
void setSharedElementExitTransition (Transition transition)
void setSharedElementReenterTransition (Transition transition)
void setSharedElementReturnTransition (Transition transition)
複製程式碼

自定義Transiton

主要重寫三個方法

  1. 開始值
  2. 結束值
  3. 建立動畫

官方示例

Tip: 針對不同的View採用不同的動畫效果可以重寫Transition

擴散(Propagation)

Propagation可以指定Transition中的檢視過渡延遲, 控制進入當前場景的檢視進入的先後順序. 例如要求Explode動畫中特定的View速度快於其他的檢視.

擴散中心

首先需要Transition確認擴散中心

void setEpicenterCallback (Transition.EpicenterCallback epicenterCallback)
複製程式碼

在Transiton.EpicenterCallback回撥中需要重寫以下方法

Rect onGetEpicenter (Transition transition)
複製程式碼

在回撥方法內返回一個矩形, 其矩形的中心即擴散中心.

關係:

  • TransitionPropagation
    • VisibilityPropagation
      • SidePropagation
      • CircularPropagation

通過transition的方法指定

void setPropagation (TransitionPropagation transitionPropagation) // 設定擴散

void setPropagationSpeed (float propagationSpeed) // 設定速度
複製程式碼

檢視原始碼可以看到Explode預設使用的CircularPropagation

Android 轉場動畫

Slide使用的自然是SlidePropagation

TransitionPropagation 屬於抽象類提供三個重寫方法

void captureValues (TransitionValues transitionValues)

String[] getPropagationProperties ()

long getStartDelay (ViewGroup sceneRoot, 
                Transition transition, 
                TransitionValues startValues, 
                TransitionValues endValues)
複製程式碼

分別控制:

  1. 捕捉

示例

slide.setPropagation(new VisibilityPropagation() {
  @Override public long getStartDelay(ViewGroup sceneRoot, Transition transition,
                                      TransitionValues startValues, TransitionValues endValues) {
    return 0;
  }
});
複製程式碼

路徑動作

PathMotion是在api21引入的過渡動畫的一種新的實現方式. 可以通過指定路徑來限制動畫運動軌跡

通過Transition設定路徑動畫

void setPathMotion (PathMotion pathMotion)
複製程式碼

PathMotion

  • ArcMotion (弧形動作)
  • PatternPathMotion

弧形軌跡

共享元素都是在不同的介面間的位置變化是以直線運動的, 通過指定以下路徑動作可以變成弧形軌跡

參考 GooglePlay 的實現效果

<changeBounds>
   <arcMotion android:minimumHorizontalAngle="15"
              android:minimumVerticalAngle="0"
              android:maximumAngle="90"/>
 </changeBounds>
複製程式碼

引數:

  • android:maximumAngle

    ​ 開始和結束的最大弧形角度

  • android:minimumHorizontalAngle

    ​ 兩者接近水平的時候最小角度

  • android:minimumVerticalAngle

    ​ 垂直同上

路徑動作

可以完全自定義路徑來控制共享元素的運動軌跡

<changeBounds>
     <patternPathMotion android:patternPathData="M0 0 L0 100 L100 100"/>
 </changeBounds>
複製程式碼

Tip: patternPathData 的引數值類似向量動畫

自定義路徑動作

 <changeBounds>
     <pathMotion class="my.app.transition.MyPathMotion"/>
 </changeBounds>
複製程式碼

相關文章