延迟共享元素的过渡动画 (part 3b)

延迟共享元素的过渡动画 (part 3b)

通过讨论 Lollipop Transition API 的一个重要的特性:延迟共享元素的过渡动画,这篇博文将继续我们关于共享元素 Transition 的深度解析。这也是我关于 Transition 这个专栏的第四篇文章。

我们通过一个常见的问题来解释为什么需要推迟某些共享元素的过渡动画。

理解问题

通常问题的根源是框架在 Activity 生命周期非常早的时候启动共享元素 Transition 。回想我们的第一篇文章,Transitions 必须捕获目标 View 的起始和结束状态来构建合适的动画。因此,如果框架在共享元素获得它在调用它的 Activity 中所给定的大小和位置前启动共享元素的过渡动画,这个 Transition 将不能正确捕获到共享元素的结束状态值,生成动画也会失败(一个过渡失败的例子Video 3.3).

Transition 开始前,能否计算出正确的共享元素的结束值主要依靠两个因素:

(1) 调用共享元素的 Activity 的布局复杂度以及布局层次结构的深度
(2)调用共享元素Activity载入数据消耗的时间

布局越复杂,在屏幕上确定共享元素的大小位置耗时越长。同样,如果调用共享元素的 Activity 依赖一个异步的数据载入,框架仍有可能会在数据载入完成前自动开始共享元素 Transition。下面列出的是你可能遇到的常见问题:

  • 存在于 Activity 托管的 Fragment 中的共享元素FragmentTransactions 在 commit 后并不会被立即执行,它们会被安排到主线程中等待执行。因此,如果共享元素存在的 Fragment 的视图层和FragmentTransaction没有被及时执行,框架有可能在共享元素被正确测量大小和布局到屏幕前启动共享元素 Transition。<a id="b1" href="#1">(1)</a>

  • 共享元素是一个高分辨率的图片。给 ImageView 设置一个超过其初始化边界的高分辨率图片,最终可能会导致在这个视图层里出现额外的布局传递,由此增加在共享元素准备好前就启动 Transition 的几率。流行的异步图片处理库比如 VolleyPicasso ,也不能可靠的解决这个问题:框架不能预先了解图片是要被下载,缩放还是在后台线程中从磁盘读取,也不管图片是否处理完毕就启动共享元素 Transition。

  • 共享元素依赖于异步的数据加载如果共享元素所需的数据是通过AsyncTaskAsyncQueryHandler,Loader或者其他类似的东西加载,在它们获得在调用它们的 Activity 的最终数据(大小、位置)前,框架就有可能在主线程中启动 Transition。

现在你可能会想:如果有办法能让暂时延迟 Transition 的使用,直到我们确定了共享元素的确切大小和位置才使用它就好了。幸好 Activity Transitions API<a id="b2" href="#2">(2)</a> 为我们提供了解决方案。

在 Activity 的onCreate()中调用postponeEnterTransition() 方法来暂时阻止启动共享元素 Transition。之后,你需要在共享元素准备好后调用 startPostponedEnterTransition 来恢复过渡效果。常见的模式是在一个OnPreDrawListener中启动延时 Transition,它会在共享元素测量和布局完毕后被调用<a id="b3" href="#3">(3)</a>。



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

    // Postpone the shared element enter transition.
    postponeEnterTransition();

    // TODO: Call the "scheduleStartPostponedTransition()" method
    // below when you know for certain that the shared element is
    // ready for the transition to begin.
}

/**
 * Schedules the shared element transition to be started immediately
 * after the shared element has been measured and laid out within the
 * activity's view hierarchy. Some common places where it might make
 * sense to call this method are:
 *
 * (1) Inside a Fragment's onCreateView() method (if the shared element
 *     lives inside a Fragment hosted by the called Activity).
 *
 * (2) Inside a Picasso Callback object (if you need to wait for Picasso to
 *     asynchronously load/scale a bitmap before the transition can begin).
 *
 * (3) Inside a LoaderCallback's onLoadFinished() method (if the shared
 *     element depends on data queried by a Loader).
 */
private void scheduleStartPostponedTransition(final View sharedElement) {
    sharedElement.getViewTreeObserver().addOnPreDrawListener(
        new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                sharedElement.getViewTreeObserver().removeOnPreDrawListener(this);
                startPostponedEnterTransition();
                return true;
            }
        });
}

忽略方法名,这里还有第二种方法可以延迟共享元素的返回 Transition,在调用Activity的onActivityReenter() 方法中延缓返回 Transition<a id="b4" href="#4">(4)</a>

/**
 * Don't forget to call setResult(Activity.RESULT_OK) in the returning
 * activity or else this method won't be called!
 */
@Override
public void onActivityReenter(int resultCode, Intent data) {
    super.onActivityReenter(resultCode, data);

    // Postpone the shared element return transition.
    postponeEnterTransition();

    // TODO: Call the "scheduleStartPostponedTransition()" method
    // above when you know for certain that the shared element is
    // ready for the transition to begin.
}

尽管添加延时可以让共享元素 Transition 更加流畅准确,但是你也要知道在应用中引入共享元素 Transition 的延迟可能会产生一些负面影响:

  • 调用postponeEnterTransition后不要忘记调用startPostponedEnterTransition
    忘记调用startPostponedEnterTransition会让你的应用处于死锁状态,用户无法进入下个Activity。
  • 不要将共享元素 Transition 延迟设置到1s以上。延迟时间过长会在应用中产生不必要的卡顿,影响用户体验。

感谢阅读!希望这篇文章对你有所帮助。

<a id="1" href="#b1">1</a>: 当然,许多应用通过调用 FragmentManager#executePendingTransactions() 来避开这个问题,这样会强制立即执行FragmentTransactions而不是异步。

<a id="2" href="#b2">2</a>: 注意!postponeEnterTransition()startPostponedEnterTransition()只对 Activity Transition起作用,对Fragment无效。详细信息可以在这里找到 StackOverflow & Google+

<a id="3" href="#b3">3</a>: 小贴士:你可以先调用 View#isLayoutRequested() 来确认是否需要调用 OnPreDrawListener,有必要的话 View#isLaidOut() 在一些情况下也能派上用场

<a id="4" href="#b4">4</a>: 在开发者选项中启用不保留 Activity 选项可以方便调试共享元素返回/重新进入时对应过渡动画的行为,这也可以帮助测试在返回的过渡效果开始之前可能发生最糟糕的情况( Activity 需要重新构造布局加载数据...)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容