Android Transition Note

HuYounger發表於2019-03-02

概述

Android 4.4.2 (API level 19)引入Transition框架,之後很多APP上都使用該框架做出很酷炫的效果,視訊中介紹了該框架的基本使用以及其中核心的一些類和方法,只有學會這些基本的API才能在之後的Activity/Fragment過渡定製一些自己想要的效果。

先看官網的一張關係圖

Android Transition Note
圖中有三個核心的類,分別是Scene、Transition和TransitionManager,下面對這個三個核心類展開分析。

Scene

Android Transition Note
Scene場景,用於儲存佈局中所有View的屬性值,建立Scene的方式可以通過getSceneForLayout方法
getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context)
比如:

mScene0 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene0, getContext());
mScene1 = Scene.getSceneForLayout(mSceneRoot, R.layout.scene1, getContext());複製程式碼

也可以直接new Scene(ViewGroup sceneRoot, View layout)

View view0 = inflater.inflate(R.layout.scene0, container, false);
View view1 = inflater.inflate(R.layout.scene1, container, false);
mScene0 = new Scene(mSceneRoot, view0);
mScene1 = new Scene(mSceneRoot, view1);複製程式碼

兩種方式都需要傳SceneRoot,即該場景的根節點。

Transition

Android Transition Note
Transition過渡動畫,前面建立了兩個場景,分別儲存了檢視的一些屬性,比如Visibility、position等,Transition就是對於這些屬性值的改變定義過渡的效果。從上圖可以看到系統內建了一些常用的Transition,Transition的建立可以通過載入xml,如:

res/transition/fade_transition.xml

<fade xmlns:android="http://schemas.android.com/apk/res/android" />複製程式碼

然後在程式碼中:

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

或者直接在程式碼中:

Transition mFadeTransition = new Fade();複製程式碼

TransitionManager

TransitionManeger用於將Scene和Transition聯絡起來,它提供了一系列的方法如setTransition(Scene fromScene, Scene toScene, Transition transition)指明起始場景和結束場景、他們的過渡動畫是什麼,go(Scene scene, Transition transition),到指定的場景所使用的過渡動畫是什麼,beginDelayedTransition(ViewGroup sceneRoot, Transition transition),在當前場景到下一幀的過渡效果是什麼。比如這裡使用go()方法,效果:
Android Transition Note
注意這裡兩個Scene中紅綠兩個方塊除了位置和大小不一樣,id是一致的,transition記錄下兩個Scene前後屬性值,根據屬性值的改變執行過渡動畫,預設情況下對SceneRoot下的所有View執行動畫效果,我們可以通過Transition.addTarget和removeTarget方法選擇性新增或移除執行動畫的View。

常用API

有時候我們只想改變當前已展示的檢視層級中View的狀態,可以通過beginDelayedTransition實現,下面列舉系統內建的Transition的使用。

AutoTransition

AutoTransition預設的動畫效果,對應xml tag為autoTransition

其實是以下幾個動畫組合順序執行:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="sequential">
    <fade android:fadingMode="fade_out" />
    <changeBounds />
    <fade android:fadingMode="fade_in" />
</transitionSet>複製程式碼

在程式碼中使用:

TransitionManager.beginDelayedTransition(mRoot, new AutoTransition());
        if (mTextView.getVisibility() != View.VISIBLE) {
            mTextView.setVisibility(View.VISIBLE);
        } else {
            mTextView.setVisibility(View.GONE);
        }複製程式碼

Android Transition Note

ChangeBounds

ChangeBounds對應xml tag為changeBounds,根據前後佈局界限的變化執行動畫

TransitionManager.beginDelayedTransition(mRoot, new ChangeBounds());
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTarget.getLayoutParams();
if ((lp.gravity & Gravity.START) == Gravity.START) {
    lp.gravity = Gravity.BOTTOM | Gravity.END;
} else {
    lp.gravity = Gravity.TOP | Gravity.START;
}
mTarget.setLayoutParams(lp);複製程式碼

Android Transition Note

ChangeClipBounds

ChangeClipBounds對應xml tag為changeClipBounds,作用物件:View的getClipBounds()值

Rect BOUNDS = new Rect(20, 20, 100, 100);
TransitionManager.beginDelayedTransition(mRoot, new ChangeClipBounds());
if (BOUNDS.equals(ViewCompat.getClipBounds(mImageView))) {
    ViewCompat.setClipBounds(mImageView, null);
} else {
    ViewCompat.setClipBounds(mImageView, BOUNDS);
}複製程式碼

Android Transition Note

ChangeImageTransform

對應xml tag為changeImageTransform,作用物件:ImageView的matrix

TransitionManager.beginDelayedTransition(mRoot, new ChangeImageTransform());
mImageView.setScaleType(ImageView.ScaleType.XXX);複製程式碼

Android Transition Note

ChangeScroll

對應xml tag為changeScroll,作用物件:View的scroll屬性值

TransitionManager.beginDelayedTransition(mRoot, new ChangeScroll());
mTarget.scrollBy(-100, -100);複製程式碼

Android Transition Note

ChangeTransform

對應xml tag 為changeTransform,作用物件:View的scale和rotation

TransitionManager.beginDelayedTransition(mRoot, new ChangeTransform());
if (mContainer2.getChildCount() > 0) {
    mContainer2.removeAllViews();
    showRedSquare(mContainer1);
} else {
    mContainer1.removeAllViews();
    showRedSquare(mContainer2);
    mContainer2.getChildAt(0).setRotation(45);
}

private void showRedSquare(FrameLayout container) {
        final View view = LayoutInflater.from(getContext())
                .inflate(R.layout.red_square, container, false);
        container.addView(view);
    }複製程式碼

Android Transition Note

Explode

對應xml tag為explode,作用物件:View的Visibility

TransitionManager.beginDelayedTransition(mRoot, new Explode());
int vis = mViews.get(0).getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE;
for (View view : mViews) {
    view.setVisibility(vis);
}複製程式碼

Android Transition Note

Fade

對應xml tag為fade,作用物件:View的Visibility

可以在初始化是指定IN或者OUT分別對應淡入和淡出,若不指定預設為淡入淡出效果

TransitionManager.beginDelayedTransition(mRoot, new Fade());
int vis = mViews.get(0).getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE;
for (View view : mViews) {
    view.setVisibility(vis);
}複製程式碼

Android Transition Note

Slide

對應xml tag為slide,作用物件:View的Visibility

TransitionManager.beginDelayedTransition(mRoot, new Slide());
int vis = mViews.get(0).getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE;
for (View view : mViews) {
    view.setVisibility(vis);
}複製程式碼

Android Transition Note

TransitionSet

對應xml tag為transitionSet

可以在程式碼中建立transitionSet如:

mTransition = new TransitionSet();
mTransition.addTransition(new ChangeImageTransform());
mTransition.addTransition(new ChangeTransform());
TransitionManager.beginDelayedTransition(mOuterFrame, mTransition);
        if (mInnerFrame.getChildCount() > 0) {
            mInnerFrame.removeAllViews();
            addImageView(mOuterFrame, ImageView.ScaleType.CENTER_CROP, mPhotoSize);
        } else {
            mOuterFrame.removeViewAt(1);
            addImageView(mInnerFrame, ImageView.ScaleType.FIT_XY,
                    FrameLayout.LayoutParams.MATCH_PARENT);
        }複製程式碼

也可以通過載入xml佈局建立transitionSet:

xml佈局長這樣:

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="together">
    <changeImageTransform/>
    <changeTransform/>
</transitionSet>複製程式碼

通過transitionOrdering屬性設定動畫執行的順序,together表示同時執行,sequential表示順序執行,在程式碼中可以呼叫TransitionSet的setOrdering(int)方法,屬性值傳ORDERING_SEQUENTIAL或者ORDERING_TOGETHER

在程式碼中:

mTransition = (TransitionSet) TransitionInflater.from(getContext()).inflateTransition(R.transition.transition);
TransitionManager.beginDelayedTransition(mOuterFrame, mTransition);
        if (mInnerFrame.getChildCount() > 0) {
            mInnerFrame.removeAllViews();
            addImageView(mOuterFrame, ImageView.ScaleType.CENTER_CROP, mPhotoSize);
        } else {
            mOuterFrame.removeViewAt(1);
            addImageView(mInnerFrame, ImageView.ScaleType.FIT_XY,
                    FrameLayout.LayoutParams.MATCH_PARENT);
        }複製程式碼

這裡結合changeImageTransform和changeTransform,效果如下:
Android Transition Note

PathMotion

Android Transition Note
Transition的輔助工具,以path的方式指定過渡效果,兩個具體實現類ArcMotion和PatternPathMotion,看下ArcMotion的效果

mTransition = new AutoTransition();
mTransition.setPathMotion(new ArcMotion());
TransitionManager.beginDelayedTransition(mRoot, mTransition);
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTarget.getLayoutParams();
        if ((lp.gravity & Gravity.START) == Gravity.START) {
            lp.gravity = Gravity.END | Gravity.BOTTOM;
        } else {
            lp.gravity = Gravity.START | Gravity.TOP;
        }
        mTarget.setLayoutParams(lp);複製程式碼

Android Transition Note
它的運動軌跡是條曲線,有興趣的可以研究下它的實現演算法,在原始碼中有個很萌的圖如下:
Android Transition Note

自定義Transition

除了系統內建的Transition,我們還可以自定義Transition效果,需要繼承Transition

public class CustomTransition extends Transition {
    @Override
    public void captureStartValues(TransitionValues values) {}

    @Override
    public void captureEndValues(TransitionValues values) {}

    @Override
    public Animator createAnimator(ViewGroup sceneRoot,
                                   TransitionValues startValues,
                                   TransitionValues endValues) {}
}複製程式碼

其工作原理是在captureStartValues和captureEndValues中分別記錄View的屬性值,官網建議確保屬性值不衝突,屬性值的命名格式參考:

package_name:transition_name:property_name複製程式碼

在createAnimator中建立動畫,對比屬性值的改變執行動畫效果,如自定義修改顏色動畫效果:
Android Transition Note

在兩個Scene中使用自定義過渡動畫,效果如下:

Android Transition Note

Note

1.Android 版本在4.0(API Level 14)到4.4.2(API Level 19)使用Android Support Library`s

2.對於 SurfaceView可能不起效果,因為SurfaceView的例項是在非UI執行緒更新的,因此會造成和其他檢視動畫不同步。

3.某些特定的轉換型別在應用到TextureView時可能不會產生所需的動畫效果。

4.繼承自AdapterView的如ListView,與該框架不相容。

5.不要對包含文字的檢視的大小進行動畫

Thanks to

Google Demo

Github Demo傳送門

相關文章