添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
3D Touch Android Android Studio AsyncTask Bubble Camera Crash Github Handler HashMap JCenter JVM Java JavaScript Looper Mac Message OkHttp RecyclerView Retrofit RxJava Terminal View VirtualAPK Volatile Widget idea plugin io java recyclerview 内存泄露 动画 多线程 异步 性能优化 排序 气泡 页面性能

Transition 概念

Transition 是指不同 UI 状态转换时的动画。其中有两个关键概念:场景(Scene)和转换(Transition)。场景定义了一个确定的 UI 布局状态,而转换定义了两个场景切换时过渡的动画。

当两个场景进行切换时,Transition 主要有下面两个行为:

1、 确定开始场景和结束场景中每个 view 的状态。
2、 根据状态差异创建 Animator,用于场景切换时每个 view 的动画。

例如最简单的对View的隐藏增加渐变动画:

1
2
TransitionManager.beginDelayedTransition(viewGroup, new Fade());
view.setVisibility(View.GONE);

动画执行的基本流程:
1、TransitionManager.beginDelayedTransition(viewGroup, new Fade()); 确定子view初始状态。
2、调用view.setVisibility(View.GONE);之后,framework会调用Transition类的captureEndValues()方法,记录每个view最新的可见状态。
3、 framework调用Transition的createAnimator()方法。transition会分析每个view的开始和结束时的数据发现view在开始时是可见的,结束时是不可见的。Fade(Transition的子类)会利用这些信息创建一个用于把view的alpha属性变为0的 Animator,并返回 Animator 对象。
4、framework会执行返回的 Animator 动画

beginDelayedTransition 方法分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
... ...
// 确定 sceneRoot 的子 view 初始状态
sceneChangeSetup(sceneRoot, transitionClone);
Scene.setCurrentScene(sceneRoot, null);

// 在下一次 sceneRoot 绘制之前,确定 view 结束状态
sceneChangeRunTransition(sceneRoot, transitionClone);
}

private static void sceneChangeRunTransition(final ViewGroup sceneRoot, final Transition transition) {
MultiListener listener = new MultiListener(transition, sceneRoot);
sceneRoot.addOnAttachStateChangeListener(listener);
// PreDraw 监听,确定结束状态
sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener);
}

private static class MultiListener implements ViewTreeObserver.OnPreDrawListener, View.OnAttachStateChangeListener {

... ...

@Override
public boolean onPreDraw() {
... ...
mTransition.captureValues(mSceneRoot, false);
if (previousRunningTransitions != null) {
for (Transition runningTransition : previousRunningTransitions) {
runningTransition.resume(mSceneRoot);
}
}

// 执行动画 调用 Transition.createAnimators() & Transition.runAnimators()
mTransition.playTransition(mSceneRoot);

return true;
}
};

页面过渡动画

过渡动画使得 Activity 跳转或者 Fragment 切换等显得不那么生硬,通过共享元素(Share Element)过渡决定了两个 Activity/Fragment 共享的视图如何在这些跳转的时候执行过渡动画。例如,如果两个 Activity 使用相同的图片(但位置和大小不同),通过 changeImageTransform 共享元素过渡就会在这些 Activity/Fragment 之间流畅地平移和缩放该图片。如图所示共享元素过渡效果:

支持的Android最低版本: Android 5.0 (API 21)

Content Transition

内容变换(Content Transition)决定了 非共享view元素 在 activity 和 fragment 切换期间是如何进入或者退出场景的。Content Transition 的触发是通过改变 view 的 visibility 来实现的。

setExitTransition() - 当A start B时,使A中的View退出场景的transition

setEnterTransition() - 当A start B时,使B中的View进入场景的transition

setReturnTransition() - 当B 返回 A时,使B中的View退出场景的transition

setReenterTransition() - 当B 返回 A时,使A中的View进入场景的transition

Shared Element Transition

共享元素变换(Shared Element Transition)决定了 共享view元素 从一个 Activity/Fragment 到另一个 Activity/Fragment t 的切换中是如何动画变化的。共享元素变换并不是真正实现了两个activity或者Fragment之间元素的共享,Framework采用了不同的方法来达到相同的视觉效果。共享元素默认其实是绘制在整个view树结构的最上层,在一个叫ViewOverlay的东西上面。

setSharedElementEnterTransition() - 设置在A进入B的时候播放的动画,共享元素以A中的位置作为起始,B中的位置为结束来播放动画。

setSharedElementReturnTransition() - 设置在B返回A的时候播放的动画,共享元素以B中的位置作为起始,A中的位置为结束来播放动画。

Activity 共享元素过渡动画

设置允许过渡动画

按官方文档的说法,使用过渡动画之前需要在 Activity 设置启用,须在 setContentView() 之前调用,最好是放在 super.onCreate() 之前,避免 6.0 及以下机型报错:requestFeature() must be called before adding content。

1
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

或者在Theme的定义里面加上

1
<item name="android:windowActivityTransitions">true</item>

开启过渡动画

例如上面示例图里面,由搜索结果页跳商详页,主图缩放效果

1、两个页面执行过渡的共享元素分别设置相同的 transitionName

1
2
3
<ImageView
...
android:transitionName="imgTransition" />

2、执行过渡动画跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
// 纯页面过渡,无共享元素,可以替代 Activity 过渡动画
startActivity(new Intent(this, DetailActivity.class), ActivityOptions.makeSceneTransitionAnimation(this).toBundle());

// 单个共享元素
startActivity(new Intent(this, DetailActivity.class),
ActivityOptionsCompat.makeSceneTransitionAnimation(this, imageView, "imgTransition").toBundle());

// 多个共享元素
Pair<View, String> pair1 = Pair.create((View) image, "imgTransition");
Pair<View, String> pair2 = Pair.create((View) title, "titleTransition");
...
ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(ActTransitionActivity.this, pair1, pair2, ...);
startActivity(new Intent(this, DetailActivity.class), transitionActivityOptions.toBundle());

3、由于一个页面布局里面 不允许有重名的 transitionName 的元素,对于列表,例如 RecyclerView,可以在 onBindViewHolder 的时候,手动设置 transitionName。

1
2
3
4
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
...
ViewCompat.setTransitionName(viewHolder.image, position + "_imgTransition");
}

那么点击跳转到商详页的时候,把对应位置的 transitionName 通过参数带过去,并设置给商详页主图 ImageView 的 transitionName。

Tips
1、页面退出时,应该调用 **Activity.finishAfterTransition()**,不能直接调用 finish(), 否则过渡动画将不执行。

2、过渡动画过程包括 Shared Element Transition Content Transition , 指共享元素 和 其他非共享元素 的过渡动画。

Fragment 共享元素过渡动画

Fragment 的过渡动画是天然支持的,在为 Fragment 添加 Transition 的时候并不需要像 Activity 一样设置 Window.FEATURE_ACTIVITY_TRANSITIONS 和 Window.FEATURE_CONTENT_TRANSITIONS。

例如,FragmentA 切换到 FragmentB,需要分别设置共享元素的 transitionName。Fragment 同样也支持设置过渡动画类型

1
2
3
4
5
6
Fragment.setSharedElementEnterTransition()
Fragment.setSharedElementExitTransition()
Fragment.setExitTransition()
Fragment.setEnterTransition()
Fragment.setReturnTransition()
Fragment.setReenterTransition()

1. 对于fragment通过 replace() 的情况:

1
2
3
4
5
6
getSupportFragmentManager()
.beginTransaction()
.addSharedElement(holder.image, "imgTransition")
.replace(R.id.container, FragmentB)
.addToBackStack(null)
.commit();

2. 对于fragment通过 add()/show()/hide() 的情况

1
2
3
4
5
6
7
8
getSupportFragmentManager()
.beginTransaction()
.addSharedElement(holder.image, "imgTransition")
.add(R.id.container, FragmentB)
.show(FragmentB)
.hide(FragmentA)
.setReorderingAllowed(true) // 必须设置,否则无效果
.commit();

过渡动画分析

页面进入和退出时,过渡动画执行顺序如图所示:

动画类型

过渡时,可以设置一些动画效果( android.transition.Transition ),一般默认是 Fade(淡入淡出)或 AutoTransition。系统还提供了其它一些动画,基本适用于大部分场景了。

  • Slide(滑动式,可以选择滑动方向)
  • Explode(分解,类似Slide,但是会根据共享元素中心做方向计算,可以理解为周围发散)
  • ChangeBounds(布局边界的变化 动画效果)
  • ChangeClipBounds(裁剪边界的变化 动画效果)
  • ChangeTransform(缩放和旋转方面的变化 动画效果)
  • ChangeImageTransform (尺寸和缩放方面的变化 动画效果)
  • AutoTransition(实际上包含了Fade-out,ChangeBounds和Fade-in的集合,对共享元素设置时包含的Fade无效果)
  • TransitionSet(实现动画集合,AutoTransition 基于此类实现)
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 页面跳转时,前一个页面非共享元素退出的动画
    getWindow().setExitTransition(new Slide());
    // 跳转返回时,前一个页面非共享元素进入的动画
    getWindow().setReenterTransition(null);

    // 页面跳转时,后一个页面非共享元素进入的动画
    getWindow().setEnterTransition(new Explode());
    // 跳转返回时,后一个页面非共享元素退出的动画
    getWindow().setReturnTransition(new Fade());

    // 前一个页面共享元素退出动画,一般不用设置,默认为move效果
    getWindow().setSharedElementExitTransition(new Fade().setDuration(1000));
    // 后一个页面共享元素进入动画
    getWindow().setSharedElementEnterTransition(new AutoTransition());

    也可以分别在两个 Activity 的 Theme 里面配置转场动画效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <style name="BaseAppTheme" parent="android:Theme.Material">
    <!-- enable window content transitions -->
    <item name="android:windowActivityTransitions">true</item>

    <!-- specify enter and exit transitions -->
    <item name="android:windowEnterTransition">@transition/explode</item>
    <item name="android:windowExitTransition">@transition/explode</item>

    <!-- specify shared element transitions -->
    <item name="android:windowSharedElementEnterTransition">
    @transition/change_image_transform</item>
    <item name="android:windowSharedElementExitTransition">
    @transition/change_image_transform</item>
    </style>
    1
    2
    3
    4
    <!-- res/transition/change_image_transform.xml -->
    <transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeImageTransform/>
    </transitionSet>

    对于上述的各种 Transition,我们也可以设置其动画时长、插值器等。

    1
    2
    3
    Transition.setDuration(300);
    Transition.setInterpolator(new FastOutSlowInInterpolator());
    Transition.setStartDelay(200);

    自定义 Transition

    若系统提供的动画无法满足需求,也可以扩展 Visibility/Transition 类实现自定义转场动画效果。例如自定义一个文字大小和颜色渐变的 Transition。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    public class ChangeTextTransition extends Transition {

    protected static final String PROPNAME_TEXTSIZE = "ChangeTextTransition::textSize";
    protected static final String PROPNAME_TEXTCOLOR = "ChangeTextTransition::textColor";

    public ChangeTextTransition() {
    addTarget(TextView.class);
    }

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
    ShareElementInfo info = ShareElementInfo.getFromView(transitionValues.view);
    if (info == null || !(info.getViewStateSaver() instanceof TextViewStateSaver)) {
    return;
    }

    // 保存 Start TextView 的字体大小及颜色值
    captureValues(transitionValues, (TextViewStateSaver) info.getViewStateSaver(), info.isEnter() ? info.getFromViewBundle() : info.getToViewBundle());
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
    ShareElementInfo info = ShareElementInfo.getFromView(transitionValues.view);
    if (info == null || !(info.getViewStateSaver() instanceof TextViewStateSaver)) {
    return;
    }

    // 保存 End TextView 的字体大小及颜色值
    captureValues(transitionValues, (TextViewStateSaver) info.getViewStateSaver(), info.isEnter() ? info.getToViewBundle() : info.getFromViewBundle());
    }

    protected void captureValues(TransitionValues value, TextViewStateSaver stateSaver, Bundle viewExtraInfo) {
    value.values.put(PROPNAME_TEXTSIZE, stateSaver.getTextSize(viewExtraInfo));
    value.values.put(PROPNAME_TEXTCOLOR, stateSaver.getTextColor(viewExtraInfo));
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
    ShareElementInfo info = endValues==null?null:ShareElementInfo.getFromView(endValues.view);
    if (info == null || !(info.getViewStateSaver() instanceof TextViewStateSaver)) {
    return null;
    }
    final TextView view = (TextView) endValues.view;

    view.setPivotX(0f);
    view.setPivotY(0f);
    float startTextSize = (float) startValues.values.get(PROPNAME_TEXTSIZE);
    final float endTextSize = (float) endValues.values.get(PROPNAME_TEXTSIZE);
    ObjectAnimator textSizeAnimator = ObjectAnimator.ofFloat(view, new TextSizeProperty(), startTextSize, endTextSize);

    int startTextColor = (int) startValues.values.get(PROPNAME_TEXTCOLOR);
    int endTextColor = (int) endValues.values.get(PROPNAME_TEXTCOLOR);
    ObjectAnimator textColorAnimator = ObjectAnimator.ofArgb(view, new TextColorProperty(), startTextColor, endTextColor);

    // 根据之前保存的 Start&End TextView 文字大小及颜色做渐变动画
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.playTogether(textSizeAnimator, textColorAnimator);
    return animatorSet;

    }

    private class TextSizeProperty extends Property<TextView, Float> {

    public TextSizeProperty() {
    super(Float.class, "textSize");
    }

    @Override
    public void set(TextView object, Float value) {
    object.setTextSize(TypedValue.COMPLEX_UNIT_PX, value);
    }

    @Override
    public Float get(TextView object) {
    return object.getTextSize();
    }
    }

    private class TextColorProperty extends Property<TextView, Integer> {

    public TextColorProperty() {
    super(Integer.class, "textColor");
    }

    @Override
    public void set(TextView object, Integer value) {
    object.setTextColor(value);
    }

    @Override
    public Integer get(TextView object) {
    return object.getCurrentTextColor();
    }
    }
    }

    Transition Overlap

    默认情况下,内容过渡动画的后一个页面的 Enter/Return 转换会在 前一个页面的 Exit/Reenter 转换结束前一点开始,产生一个小的重叠来让整体的效果更自然、更协调。默认 overlap 是 true,进入转换会退出转换开始后尽可能快地开始,如果设置为 false,进入转换只能在退出转换结束后开始,通常都为 true。

    1
    2
    getWindow().setAllowEnterTransitionOverlap(true);
    getWindow().setAllowReturnTransitionOverlap(true);

    Shared Element Overlay

    默认情况下,共享元素视图是绘制在整个视图结构之上的(的 ViewOverlay 层)。但是如果共享元素周围有点击效果,例如 ?attr/selectableItemBackground 波纹效果,那么如果共享元素视图绘制在整个视图结构之上,点击时在波纹效果还没消失的时候,会堆叠一个共享元素视图,比较影响协调性,可以把 sharedElementsUseOverlay 置为 false。

    1
    getWindow().setSharedElementsUseOverlay(false)

    更新共享元素

    如上图所示,共享元素过渡跳转到预览页后,我们可以通过 ViewPager 切换到其他元素,那么返回时如果未更新共享元素对应关系,则返回时会出现找不到对应的共享元素,而无法执行过渡动画。理想情况如下图所示:

    那么我们可以通过 SharedElementCallback 来更新共享元素对应关系。

    对于列表页 ListActivity:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    setExitSharedElementCallback(new SharedElementCallback() {

    private void removeOldViews(List<String> names, Map<String, View> sharedElements) {
    if (!names.isEmpty()) {
    for (String name : names) {
    sharedElements.remove(name);
    }
    names.removeAll(namesTobeRemoved);
    }
    }

    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
    // 清除旧的共享元素关系
    removeOldViews(names, sharedElements);
    ViewHolder viewHolder = recyclerView.findViewHolderForPosition(currentPosition);
    View imageView = viewHolder.itemView.findViewById(R.id.image);

    // 更新共享元素对应关系
    names.add(imageView.getTransitionName());
    sharedElements.put(imageView.getTransitionName(), imageView);
    }
    });

    对于预览页 PreviewActivity:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    ActivityCompat.setExitSharedElementCallback(this, new SharedElementCallback() {

    private void removeOldViews(List<String> names, Map<String, View> sharedElements) {
    if (!names.isEmpty()) {
    for (String name : names) {
    sharedElements.remove(name);
    }
    names.removeAll(namesTobeRemoved);
    }
    }

    @Override
    public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
    // 清除旧的共享元素关系
    removeOldViews(names, sharedElements);
    Fragment fragment = getCurrentFragment();
    if(fragment != null) {
    View view = fragment.getView();
    ImageView imageView = view.findViewById(R.id.image);

    // 更新共享元素关系
    sharedElements.put(imageView.getTransitionName(), imageView);
    names.add(imageView.getTransitionName());
    }
    }
    });

    Tips
    1、在PreviewActivity ViewPager 切换时,需要把当前的 position 通知给 ListActivity,RecyclerView 自动滚动到对应 position 位置,返回时才能获取到对应的 ViewHolder
    2、若不希望实时更新 position,也可以在预览页返回的时候的时候再更新,那么可能就需要延迟过渡动画,等待 RecyclerView 滚动到对应位置绘制完成,再执行过渡动画。对于 Activity 可以通过 postponeEnterTransition() startPostponedEnterTransition() 来停止和恢复过渡动画,对于 Fragment 可以通过 getActivity().supportPostponeEnterTransition() getActivity().supportStartPostponedEnterTransition() 来停止和恢复过渡动画

    布局变化过渡动画

    布局变化自动监听

    1
    TransitionManager.beginDelayedTransition(viewGroup)

    当我们调用 TransitionManager.beginDelayedTransition(viewGroup) 方法时,会立即记住当前 viewGroup 底下子节点的状态,然后在下一帧中再次记录 viewGroup 所有子节点的状态,根据状态差异执行过渡动画。默认动画是 AutoTransition。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // CHANGE SIZE
    TransitionManager.beginDelayedTransition(rootViewGroup);
    ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams();
    if (sizeChanged) {
    layoutParams.width = 200;
    layoutParams.height = 200;
    } else {
    layoutParams.width = 100;
    layoutParams.height = 100;
    }
    imageView.setLayoutParams(layoutParams);

    // CHANGE POSITION
    TransitionManager.beginDelayedTransition(rootViewGroup, new AutoTransition());

    LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams();
    if (locationChanged) {
    layoutParams.gravity = Gravity.CENTER;
    } else {
    layoutParams.gravity = Gravity.LEFT;
    }
    imageView.setLayoutParams(layoutParams);

    animateLayoutChanges 属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:animateLayoutChanges="true">

    <ImageView
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:src="@mipmap/ic_launcher"/>

    <TextView
    android:id="@+id/tvText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="some text" />

    </LinearLayout>

    通过布局属性 animateLayoutChanges,可以为子 View 变化自动执行过渡动画。如图所示:

    场景过渡框架

    前文主要说明 Activity 之间跳转的过渡动画,那么如果在同一个 Activity 内的各个组件之间打造过渡动画效果,就可以通过场景过渡框架。场景过渡有两个关键概念:场景(Scene)和转换(Transition),简单的说就是每个 Scene 提供对应的UI布局组件,Transition 负责 Scene 执行两个 Scene 之间变换的过渡动画执行。对于两个场景之间添加过渡动画效果流程如下:

    1、为起始布局和结束布局分别创建一个 Scene 对象。
    2、创建一个 Transition 对象以定义所需的动画类型。
    3、调用 TransitionManager.go(),然后系统会运行动画以交换布局。

    布局变化场景动画

    为两个布局创建Scene。

    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scene_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <include layout="@layout/a_scene" />
    </FrameLayout>

    第一个场景布局:res/layout/a_scene.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
    android:id="@+id/text_view1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Text Line 1" />

    <TextView
    android:id="@+id/text_view2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Text Line 2" />
    </LinearLayout>

    第二个场景布局:res/layout/b_scene.xml,更改了布局方向和文本排列方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
    android:id="@+id/text_view2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Text Line 2" />

    <TextView
    android:id="@+id/text_view1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Text Line 1" />
    </LinearLayout>

    注意:两个场景布局需要做过渡动画的节点id需要保持一致!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class SceneTransitionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_scene_transition);

    ViewGroup sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

    Scene aScene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this);
    final Scene bScene = Scene.getSceneForLayout(sceneRoot, R.layout.b_scene, this);

    sceneRoot.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    // 执行场景动画
    TransitionManager.go(bScene, new ChangeBounds());
    }
    });
    }
    }

    执行场景动画效果如图所示: