您的位置:首页 > 移动开发 > Android开发

Android SharedElement详解

2016-04-11 11:16 1006 查看
转至:http://blog.csdn.net/mr_wrong1/article/details/49669275

概述

SharedElement是什么,翻译是共享元素,但是光说也说不出个杰宝来,no picture say a jb,先看一下效果图,省好多话。



这个效果目前只支持API21以上,之前的可以使用一些兼容库,比如这个ActivityOptionsICS。废话少说,下面来介绍一下如何去使用和实现这个效果。

简单原理

俗话说眼见为实,这句话也不一定对,从上面的图可以看到,从activityA的一个图片,变大进入到了activityB里面。好像是把A的图片传递给了B去显示。其实几乎所以的变换都是在B里面完成的,A并没有干什么卵事情。简单的说就是把A里面的图片的位置,大小等信息传递给B,然后B通过这些信息在自己的界面里面绘制出一模一样的,然后在通过比较最终的大小和位置等信息,创建出一个Animator,再然后就是动画的执行。这就看起来像是A里面的元素共享到了B里面。当然具体的过程远比这复杂的多。

实现步骤

要使用共享元素,要先在调用和被调用的Activity里面声明,
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
,注意这句话要在
setContentView
之前调用。也可以在主题里面声明
<item name="android:windowContentTransitions">true</item>

想要共享的元素要有相同的
TransitionName
,可以在XML文件里面声明,或者在代码里面
setTransitionName()
。这里使用了
RecyclerView
要去设置
TransitionName
就应该去
adapter
里面设置。这里使用了
AppCompatActivity
,所以可以
ViewCompat.setTransitionName(holder.imageView,
image.getUrl());
,当然也可以
holder.imageView.setTransitionName(image.getUrl());
。这个要确保A,B里面要共享的元素拥有相同的
TransitionName
,否则
framework
将不知道怎么去变换。
然后就可以去
startActivity
了,但是这里的
startActivity
还需要一个
Bundle
对象,用于传递共享元素的一些信息,例如:4.

<code class="hljs avrasm has-numbering">ActivityOptionsCompat options = ActivityOptionsCompat
<span class="hljs-preprocessor">.makeSceneTransitionAnimation</span>(this, view, mAdapter<span class="hljs-preprocessor">.get</span>(position)<span class="hljs-preprocessor">.getUrl</span>())<span class="hljs-comment">;</span>
startActivity(intent, options<span class="hljs-preprocessor">.toBundle</span>())<span class="hljs-comment">;</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

5.其实现在就已经可以实现简单的共享元素的效果了,就是这么简单。但是上面的效果图可以看到,当切换到其他的图片的时候,依然可以正确的”共享”回来,这就不是简单是设置
TransitionName
这么简单了。

完善效果

首先要先弄明白两个Activity的转换是分几种情况的:

开始时从A进入B:

1.A退出(exit) ,A中的View播放动画

2.B进入(enter) ,B中的View播放动画

当从B退回A时:

1.B返回(return) ,B中的View播放动画

2.A重新进入(reenter) ,A中的View播放动画

其实当进入带ActivityB的时候,是应该调用
setEnterSharedElementCallback(SharedElementCallback)
,当使用简单的共享元素的时候,可以不写这句话,framwork已经帮我们实现好了。这里写这个是因为有时(滑动页面之后)要返回非进入的共享元素,要去回调过去。当然callback也是可以选择实现方法的,一般没有什么效果的只要实现onMapSharedElements就可以了。再来说一下常用的实现方法:

onMapSharedElements

装载共享元素
onSharedElementStart

是共享元素开始时候回调,一般是进入的时候使用
onSharedElementEnd

是共享元素结束的时候回调,一般是退出的时候使用

当然还有其他的方法,具体可以看看文档。
每次进入和退出都会回调SharedElementCallback,所以一般来说在
onSharedElementStart
onSharedElementEnd
里面要去判断是返回还是进入操作,当进入时,执行
onSharedElementStart
,返回时调用
onSharedElementEnd
。如何去判断是返回还是进入呢,可以在
finishAfterTransition
方法中进行判断,当执行返回操作时,一般是back键时,判断是返回操作,然后去执行
onSharedElementEnd
里面的方法,否则去执行
onSharedElementStart
里面的方法。同时,要去返回一些共享元素的信息也可以在这里回调回去。比如这里的图片的position。

当返回到activityA的时候,要去获取activityB返回的信息,可以在
onActivityReenter(int requestCode, Intent data)
方法里面获取,比如这里获取到返回的position信息,然后去把
RecyclerView
滑动到响应的位置。同样的,activityA是要设置
setExitSharedElementCallback
的。这样才能响应到返回的共享元素

当然,这么说还是很抽象的,有兴趣可以去看一下我的一个开源项目,GitHub地址,欢迎star和提意见。

这里还有一些学习共享元素的开源项目
activity-transitions

MaterialTransitions

Material-Animations

同时也欢迎访问我的个人博客 http://70kg.info

转至:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0201/2394.html



本文将深入分析共享元素变换(shared element transition)以及它在Activity 和Fragment Transitions API中所扮演的角色。这是这Transition系列文章的第三部分:

第一章:
Activity和Fragment Transition介绍

第二章:
深入理解内容变换(Content Transition)

第三章上: 深入理解共享元素变换(Shared Element Transition)

第三章下: Shared Element Transitions In Practice (即将发布)

第四章: Activity & Fragment Transition Examples (即将发布)

第三章(本章)将分为两部分:上着重底层的原理,下着重api的具体实现,比如延迟某些元素变换的重要性以及
SharedElementCallback
s的实现。

什么是共享元素变换?

元素共享式变换(shared element transition)决定了共享的view元素从一个Activity/Fragment 到另一个Activity/Fragment t的切换中是如何动画变化的。共享元素在被调用Activity进入和返回时播放动画,共享元素在进入和返回时的变换效果通过window和Fragment的如下方法来设置:

进入:

setSharedElementEnterTransition()

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

返回:

setSharedElementReturnTransition()

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

 

注意,Activity Transition API 也可以使用 setSharedElementExitTransition() 和setSharedElementReenterTransition()方法分别设置共享元素的exit 和reenter 变换。但是一般来讲这是不必要的。如果你想看先关的例子,可以查看这篇博客this blog post.至于为什么Fragment中没有共享元素的exit 和reenter 变换,请查看George Mount在stackoverflow上的回答:this StackOverflow post

 



上图演示了google play music应用中的共享元素变换效果。变换包含了两个共享的view元素:一个ImageView以及他的父亲CardView。ImageView在两个activity之间无缝的动画切换,而CardView则是渐渐的扩展到界面上。

在第一章中我们简单的介绍了这个话题,这篇文章则是更深入的去分析共享元素变换(shared element transition)。共享元素变换的原理是什么?有哪些共享元素变换效果可用?共享元素变换动画是如何绘制的,又是在哪里绘制的?接下来的小节中我们将一一回答。

共享元素变换揭秘

从前两篇文章中我们知道,一个变换(Transition )主要有两方面的职责:

捕获view开始和结束状态以及创建一能在两个状态间渐变的动画。共享元素变换没有什么不同。在共享元素变换开始之前,必须首先捕获每个共享元素的开始和结束状态(调用activity以及被调用activity中的位置、大小、外观),有了这些信息才能决定每个共享元素的入场动画。

深入理解Content Transition 中类似,framework的共享元素变换是通过运行时改变其属性实现的,当Activity A 调用 Activity B ,发生的事件流如下:

1.Activity A调用startActivity(), Activity B被创建,测量,同时初始化为半透明的窗口和透明的背景颜色。

2.framework重新分配每个共享元素在B中的位置与大小,使其跟A中一模一样。之后,B的进入变换(enter transition)捕获到共享元素在B中的初始状态。

3.framework重新分配每个共享元素在B中的位置与大小,使其跟B中的最终状态一致。之后,B的进入变换(enter transition)捕获到共享元素在B中的结束状态。

4.B的进入变换(enter transition)比较共享元素的初始和结束状态,同时基于前后状态的区别创建一个Animator(属性动画对象)。

5.framework 命令A隐藏其共享元素,动画开始运行。随着动画的进行,framework 逐渐将B的activity窗口显示出来,当动画完成,B的窗口才完全可见。

与内容变换(content transition)取决于view的可见性不同(visibility),共享元素变换取决于每个共享元素的位置、大小以及外观。在api 21中,框架层提供了几个Transition 的实现,可以用于定义共享元素在场景中的切换效果。

ChangeBounds -捕获共享元素的layout bound,然后播放layout bound变化动画。ChangeBounds 是共享元素变换中用的最多的,因为前后两个activity中共享元素的大小和位置一般都是不同的。

ChangeTransform -  捕获共享元素的缩放(scale)与旋转(rotation)属性 ,然后播放缩放(scale)与旋转(rotation)属性变化动画。

ChangeClipBounds -  捕获共享元素clip bounds,然后播放clip bounds变化动画。

ChangeImageTransform -  捕获共享元素(ImageView)的transform matrices 属性,然后播放ImageViewtransform matrices 属性变化动画。与ChangeBounds相结合,这个变换可以让ImageView在动画中高效实现大小,形状或者ImageView.ScaleType 属性平滑过度。

@android:transition/move -  将上述所有变换同时进行的一个TransitionSet 。就如在第一章中所讲的一样,如果共享元素的进入和返回变换没有特别声明,框架将使用它作为默认的变换。

我们可以看到,共享元素变换并不是真正实现了两个activity或者Fragment之间元素的共享,实际上我们看到的几乎所有变换效果中(不管是B进入还是B返回A),共享元素都是在B中绘制出来的。Framework没有真正试图将A中的某个元素传递给B,而是采用了不同的方法来达到相同的视觉效果。A传递给B的是共享元素的状态信息。B利用这些信息来初始化共享View元素,让它们的位置、大小、外观与在A中的时候完全一致。当变换开始的时候,B中除了共享元素之外,所有的其他元素都是不可见的。随着动画的进行,framework 逐渐将B的activity窗口显示出来,当动画完成,B的窗口才完全可见。

使用共享元素的 Overlay

最后,我们需要讨论一下shared element overlay这个概念才算是对共享元素变换的绘制过程有了一个完整的了解。

虽然不是非常明显的可以看到,共享元素默认其实是绘制在整个view树结构的最上层,在一个叫ViewOverlay的东西上面。你可能没听说过ViewOverlay,他是4.3之后才有的一个新类,它是view的最上面的一个透明的层,添加到ViewOverlay上面的Drawable和view可以被绘制到任何东西的上面,甚至是ViewGroup的子元素。这似乎可以解释为什么framework 会选择ViewOverlay来作为共享元素变换的绘制空间了。

-关于ViewOverlay,除了官方解释还可以看看这篇文章:ViewOverlay与animation介绍 

虽然共享元素默认是绘制在ViewOverlay上面,但是framework 还是提供了关闭这个选项的功能,调用Window的setSharedElementsUseOverlay(false) 方法。这样主要是为了防止万一有这样的开发需要。如果你选择了关闭overlay,那么请注意这是有一定副作用的。



在上图的效果中,我们运行了两次不同方式的动画,第二次便是关闭overlay之后的效果,我们可以明显的看到这导致了一个问题。

总之除非有一万个理由,否则不要关闭shared element overlay。

总结

这篇文章涵盖了如下几个要点:
1.元素共享式变换(shared element transition)决定了共享的view元素从一个Activity/Fragment 到另一个Activity/Fragment t的切换中是如何动画变化的。
2.共享元素变换取决于每个共享元素的位置、大小以及外观。

3.共享元素默认其实是绘制在整个view树结构的最上层,在一个叫ViewOverlay的东西上面。

4.共享元素变换并不是真正实现了两个activity或者Fragment之间元素的共享,Framework采用了不同的方法来达到相同的视觉效果。

 

注:作者在github上上传了一个transaction的demo,虽然没有在文章中明确说明,但我觉得就是这系列文章的相关demo:activity-transitions

本文翻译自
Shared Element Transitions In-Depth (part 3a) 

欢迎转载,但请保留本文链接 http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0201/2394.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: