深入理解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退出的时候,屏幕被分成两半,各自消失在上下边缘。

89464722.gif

到目前位置我们只是肤浅的勾勒出了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().

    1.framework遍历A的View树,确定当A的exit transition运行时哪些view会退出场景(即哪些view是transitioning view)。

    2.A的exit transition捕获A中transitioning view的开始状态。

    3.framework将A中所有的transitioning view设置为INVISIBLE。

    4.A的exit transition捕获到A中transitioning view的结束状态。

    5.A的exit transition比较每个transitioning view的开始和结束状态,然后根据前后状态的区别创建一个Animator。Animator开始运行,同时transitioning view退出场景。

二、Activity B启动.

    1.framework遍历B的View树,确定当B的enter transition运行时哪些view会进入场景,transitioning view会被初始化为INVISIBLE。

    2.B的enter transition捕获B中transitioning view的开始状态。

    3.framework将B中所有的transitioning view设置为VISIBLE。

    4.B的enter transition捕获到B中transitioning view的结束状态。

    5.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的实现:FadeSlide, 和 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<View> 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来实现将屏幕分成两半的效果。

998777744.gif

有时候transition groups被用来修改一些Activity切换是出现的莫名其妙的bug。还是以上图为例,calling Activity 显示了封面图片的相册界面,而被调用activity则显示了一个header的背景图片,共享的封面图片,一个webview。这个app使用了类似与Google Play Games的transition:从中间成两半,各自滑倒上下边缘。但是,仔细观察你会发现只有上部分有滑动的动画效果,Webview没有。

那么问题来了,到底是哪里没对?上面的结果证明WebView虽然是一个ViewGroup但是没有被系统认为是transition view。因此content transition没有在它上面运行。幸运的是我们可以在return transition之前的某个地方调用

webView.setTransitionGroup(true)

来解决这个问题。

总结

总的来说,本文涉及到了三个重要的方面:

1.content transition决定非共享元素(即transitioning view)在Activity切换的时候是如何变换的。

2.Content transition的触发是通过改变transitioning view的visibility来实现的。

3.Transition group让我们可以将ViewGroup作为一个整体来变换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值