深入理解Content Transition

Home / Android MrLee 2015-1-23 3465

本文将对content transition以及其在Activity和Fragment Transition API中扮演的角色做一个深入分析。这是此话题系列的第二章节:
第一章: Activity和Fragment Transition介绍
第二章: 深入理解内容变换(Content Transition)
第三章上: 深入理解共享元素变换(Shared Element Transition)
第三章下: Shared Element Transitions In Practice (即将发布)
第四章: Activity & Fragment Transition Examples (即将发布)
从回顾上一章中学到的关于content transition的知识开始。

什么是Content Transition

content transition决定了非共享view元素在activity和fragment切换期间是如何进入或者退出场景的。根据google最新的Material Design设计语言,content transition让我们毫不费力的去协调Activity/Fragment切换过程中view的进入和退出,让这个过程更流畅。在5.0之后content transition可以通过调用Window和Fragment的如下代码来设置:
ps:还记得上篇文章中对A和B的约定吗
(1)setExitTransition() - 当A start B时,使A中的View退出场景的transition
(2)setEnterTransition() - 当A start B时,使B中的View进入场景的transition
(3)setReturnTransition() - 当B 返回 A时,使B中的View退出场景的transition
(4)setReenterTransition() - 当B 返回 A时,使A中的View进入场景的transition
以下图为例,演示了google play Games app如何通过content transition实现activity之间的平滑切换。当第二个activity开始的时候,enter transition让用户的头像从底部边缘慢慢滑入。而在activity退出的时候,屏幕被分成两半,各自消失在上下边缘。

1421395105402519


到目前位置我们只是肤浅的勾勒出了content transition轮廓,有几个非常重要的问题仍然存在。content transition触发的内部机制,有哪些Transition类可用?framework如何确定哪些view是transitioning view?ViewGroup和它的孩子可以被作为一个整体播放动画吗?,我们将逐个解答。

Content Transition内部揭秘

回忆上篇文章的内容,一个Transition主要有两个职责:捕获目标view的开始和结束时的状态、创建一个用于在两个状态之间播放动画的Animator。Content transition同样如此:在Content transition动画(animation)创建之前,framework必须通过设置transitioning view的visibility将动画需要的状态信息告诉animation。具体来说,当Activity A startsActivity B之时,发生了如下的事件:
一、Activity A 调用startActivity().
    framework遍历A的View树,确定当A的exit transition运行时哪些view会退出场景(即哪些view是transitioning view)。
A的exit transition捕获A中transitioning view的开始状态。
framework将A中所有的transitioning view设置为INVISIBLE。
A的exit transition捕获到A中transitioning view的结束状态。
A的exit transition比较每个transitioning view的开始和结束状态,然后根据前后状态的区别创建一个Animator。Animator开始运行,同时transitioning view退出场景。
二、Activity B启动.
    framework遍历B的View树,确定当B的enter transition运行时哪些view会进入场景,transitioning view会被初始化为INVISIBLE。
B的enter transition捕获B中transitioning view的开始状态。
framework将B中所有的transitioning view设置为VISIBLE。
B的enter transition捕获到B中transitioning view的结束状态。
B的enter transition比较每个transitioning view的开始和结束状态,然后根据前后状态的区别创建一个Animator。Animator开始运行,同时transitioning view进入场景。
通过在每个transitioning view中来回切换INVISIBLE 和VISIBLE,framework确保content transition得到创建animation(期望的animation)所需的状态信息。显然content Transition对象需要在开始和结束场景中都能记录到transitioning view的visibility。 非常幸运的是抽象类Visibility已经为你做了这些工作:Visibility的子类只需要实现onAppear()onDisappear() 两个工厂方法,在这两个工厂方法中创建并返回一个进入或者退出场景的Animator对象。在api 21中,有三个现成的Visibility的实现:Fade, Slide, 和 Explode
他们都可以用在Activity 和 Fragment中创建content transition。如果必要,还可以自定义Visibility,这将在今后的文章中讲解。

Transitioning Views以及Transition Groups

到目前为止,我们假设了content transition是操作非共享元素的(即提到了很多次的transitioning view)。在本节,我们将讨论framework是如何决定transitioning view的集合以及如何使用transition groups自定义它。
在transition开始之前,framework通过递归遍历Activity(或者Fragment)的Window中的View树来决定transitioning view的集合。整个搜索过程开始在根view中调用ViewGroup的captureTransitioningView方法,captureTransitioningView的代码如下:
/** @hide */
@Override
public void captureTransitioningViews(List transitioningViews) {
    if (getVisibility() != View.VISIBLE) {
        return;
    }
    if (isTransitionGroup()) {
        transitioningViews.add(this);
    } else {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            child.captureTransitioningViews(transitioningViews);
        }
    }
}

这个递归的过程比较简单粗暴:framework跟踪不同级别的view树直到它找到一个VISIBLE的叶子view或者是一个transition group。transition group允许我们将整个ViewGroup作为一个整体来变换。如果一个ViewGroup的isTransitionGroup()方法返回true,则它的所有孩子都将被视为一个整体一起播放动画。否则将会继续递归该ViewGroup,其子view也会在动画的时候被单独对待。遍历的最后结果是一个完整的transitioning view的集合将在content transition的时候播放动画。
注:默认情况下,isTransitionGroup()将在ViewGroup有背景或者有transition name的时候返回true(参见documentation 中对该方法的声明)。
以下图为例,在整个过程中,用户的头像先是作为一个单独的元素渐渐的进入到下一个界面,而在返回的时候他又是和其他元素一起作为一个整体被动画。google play Games 中貌似用的是transition group来实现将屏幕分成两半的效果。

1421395183295370

有时候transition groups被用来修改一些Activity切换是出现的莫名其妙的bug。还是以上图为例,calling Activity 显示了封面图片的相册界面,而被调用activity则显示了一个header的背景图片,共享的封面图片,一个webview。这个app使用了类似与Google Play Games的transition:从中间成两半,各自滑倒上下边缘。但是,仔细观察你会发现只有上部分有滑动的动画效果,Webview没有。
那么问题来了,到底是哪里没对?上面的结果证明WebView虽然是一个ViewGroup但是没有被系统认为是transition view。因此content transition没有在它上面运行。幸运的是我们可以在return transition之前的某个地方调用
1 webView.setTransitionGroup(true) 来解决这个问题。

总结

总的来说,本文涉及到了三个重要的方面:
1.content transition决定非共享元素(即transitioning view)在Activity切换的时候是如何变换的。
2.Content transition的触发是通过改变transitioning view的visibility来实现的。
3.Transition group让我们可以将ViewGroup作为一个整体来变换。

本文链接:https://www.it72.com/914.htm

推荐阅读
最新回复 (0)
返回