1 ViewOverlay
ViewOverlay是Android 4.3以后(API 18+)新增的一个类,它是view的最上面的一个透明的层,我们可以在这个层之上添加内容而不会影响到整个布局结构。这个层和我们的界面大小相同,可以理解成一个浮动在界面表面的二维空间。其实,ViewOverlay能做的事情RelativeLayout都能够实现,只是ViewOverlay提供了一种更友好的方式,它上面的任何view都不会响应onTouch()和onClick()事件。最重要的是,利用ViewOverlay可以让某个view在任意的布局元区域播放动画,不管该view是否属于区域的子view,后面的Transition和共享元素都是在ViewOverlay之上实现的。
ViewOverlay是如何工作的呢?首先任何view我们都可以通过getOverlayView()方法获得ViewOverlay,然后通过add()、remove()、clear()等方法对里面的子view进行操作。
如下图所示,就是使用ViewOverlay所达到的效果:
2 Scene
Scene(场景)被用来记录视图中所有view和它的属性值, API提供有两种方法可以创建场景,用来记录视图。
a. 直接用new Scene()的方法;
|
或者
|
b. 使用静态方法;
|
注意:不管使用上面那种方式,都要使用到mSceneRoot
(场景的根目录),而mSceneRoot
在动画启动的时候,都会调用remove掉所有的子view,然后再加载结束场景里面的view,所以对于使用第一种方式创建场景的时候(特别是结束场景),一定要确保这个view是mSceneRoot的直接子view,或者这个view没有parentview,否则会报错。
3 Transition
3.1 使用Transition进行场景切换
首先,我们来看一个简单的例子,下方就是效果图:
这是一个简单的动画,三个图形按照顺时针的方向依次转动,如何实现的呢?以前的方法就是给每个view设置位移动画,而使用Transition的话,就会特别简单:
首先来定义一个activity,用来展示视图
<?xml version="1.0"encoding="utf-8"?>
|
定义一个起始布局,scene1.xml
<?xml version="1.0"encoding="utf-8"?> |
定义一个结束时布局,scene2.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="400dp"> <ImageView android:id="@+id/image3" android:layout_width="150dp" android:layout_height="150dp" android:layout_centerInParent="true" android:src="@drawable/image3" /> <ImageView android:id="@+id/image2" android:layout_width="150dp" android:layout_height="150dp" android:layout_alignParentLeft="true" android:layout_below="@id/image3" android:src="@drawable/image2" /> <ImageView android:id="@+id/image1" android:layout_width="150dp" android:layout_height="150dp" android:layout_alignParentRight="true" android:layout_below="@id/image3" android:alpha="20" android:src="@drawable/image1" /> </RelativeLayout> |
现在开启动画
//加载Scene scene2 = Scene.getSceneForLayout(rootView, R.layout.scene2, this); TransitionManager.go(scene2, new ChangeBounds()); |
TransitionManager中的go()方法如果只传一个场景参数,如TransitionManager.go(scene2);系统会默认传入一个new AutoTransition(),当然我们也可以通过传入一个transition来控制整个场景动画的效果或者场景中某个view的动画效果,其中ChangeBounds和AutoTransition都是transition的子类,后面会详细讲到。
总的来说,就是先定义一个起始场景和结束场景(我们在以前的动画中称之为关键帧),然后通过TransitionManager的方法来进行场景切换。
3.2 使用Transition控制某个View的变化
很多时候,我们只需要改变当前已经展示的视图中的某个view,这时候就可以使用延迟动画。
a. Slide(划动)
//向右滑出 Slide slide = new Slide(Gravity.RIGHT); //设置滑动的时间 slide.setDuration(1000); TransitionManager.beginDelayedTransition(mViewGroup, slide); text.setVisibility(visible ? View.VISIBLE : View.GONE); visible = !visible; |
b. Fade(淡入淡出)
//默认是淡入淡出 TransitionManager.beginDelayedTransition(mViewGroup); // TransitionManager.beginDelayedTransition(mViewGroup,new Fade()); text.setVisibility(visible ? View.VISIBLE : View.GONE); visible = !visible; |
c. Explode and Propagation (粒子扩散)
使用 Explode 可以做粒子扩散的效果,粒子扩散的中心点可以通过setEpicenterCallback 方法设定。具体扩散的效果可以通过TransitionPropagation 设定,TransitionPropagation 会计算每个动画的开始延迟时间。比如默认情况下Explode 使用的CircularPropagation,这个是一个圆形扩散效果,每个元素执行扩散动画的延迟时间是其距中心的距离决定的。我们使用setPropagation 方法就可以设置TransitionPropagation.
@Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // save rect of view in screen coordinates final Rect viewRect = new Rect(); //获取view所在屏幕坐标中的可是区域 view.getGlobalVisibleRect(viewRect); // create Explode transition with epicenter Explode explode = new Explode(); explode.setEpicenterCallback(new Transition.EpicenterCallback() { @Override public Rect onGetEpicenter(Transition transition) { return viewRect; } }); explode.setDuration(1000); TransitionManager.beginDelayedTransition(gridView, explode); // remove all views from Recycler View gridView.setAdapter(null); } |
d. ChangeImageTransform(改变图片大小)
ChangeImageTransform可以对一个图片的矩阵信息进行变换,当我们改变ImageView的 scaleType 属性时,就非常有用。很多时候我们可以结合 ChangeBounds 来改变位置,大小以及 scaleType。
TransitionManager.beginDelayedTransition(transitionsContainer,newTransitionSet() .addTransition(newChangeBounds()) .addTransition(newChangeImageTransform())); ViewGroup.LayoutParamsparams= imageView.getLayoutParams(); params.height = expanded ?ViewGroup.LayoutParams.MATCH_PARENT : ViewGroup.LayoutParams.WRAP_CONTENT; imageView.setLayoutParams(params); imageView.setScaleType(expanded ?ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.FIT_CENTER); |
e. Path (路径)
使用 setPathMotion 方法,可以在任意两点之间的位置变换做路径动画,比如使用 ChangeBounds 改变 View的位置:
TransitionManager.beginDelayedTransition(transitionsContainer, newChangeBounds().setPathMotion(newArcMotion()).setDuration(500)); FrameLayout.LayoutParamsparams=(FrameLayout.LayoutParams) button.getLayoutParams(); params.gravity = isReturnAnimation ?(Gravity.LEFT |Gravity.TOP): (Gravity.BOTTOM |Gravity.RIGHT); button.setLayoutParams(params); |
与此同时,配置 Transitions 也非常容易,你可以给一些特殊目标的 View 指定 Transitions,仅仅只有它们才能有动画.
增加动画目标:
· addTarget(View target) .view
· addTarget(inttargetViewId). 通过view的id
· addTarget(StringtargetName) .与 TransitionManager
.setTransitionName 方法设定的标识符相对应。
· addTarget(ClasstargetType) .类的类型,比如Android.widget.TextView.class。
移除动画目标:
· removeTarget(View target)
· removeTarget(int targetId)
· removeTarget(StringtargetName)
· removeTarget(Class target)
排除不想做动画的view:
· excludeTarget(View target,boolean exclude)
· excludeTarget(int targetId,boolean exclude)
· excludeTarget(Class type,boolean exclude)
· excludeTarget(Class type,boolean exclude)
排除某个 ViewGroup 的所有子 View:
· excludeChildren(Viewtarget, boolean exclude)
· excludeChildren(inttargetId, boolean exclude)
· excludeChildren(Class type,boolean exclude)
当然也可以使用xml文件来设置transition,需要将 Translation 资源放在 res/anim目录,例如:
<?xml version="1.0" encoding="utf-8"?> <transitionSetxmlns:app="http://schemas.Android.com/apk/res-auto" app:transitionOrdering="together" app:duration="400"> <changeBounds/> <changeImageTransform/> <fade app:fadingMode="fade_in" app:startDelay="200"> <targets> <targetapp:targetId="@id/transition_title"/> </targets> </fade> </transitionSet> // And inflating: TransitionInflater.from(getContext()).inflateTransition(R.anim.my_the_best_transition); |
我们还可以自己定义Transition,自定Transitions,我们需要实现三个方法:captureStartValues,captureEndValues和 createAnimator.前面两个方法用来捕捉 view在转场前后的状态。
4 共享元素
在Android 5.0(API 21) 之后,基于Transition的动画被运用到Activity和Fragment控件之间的切换上。
4.1 Activity之间的切换
a.设置两个Activity的theme:(非必要,不使用的时候,目前没有发现有什么问题)
android:theme="@style/AppTheme" |
<style name="AppTheme" parent="Theme.AppCompat"> <!-- Customize your theme here. --> <item name="android:windowContentTransitions">true</item> </style> |
b. 在xml布局中需要共享元素的view都设置transitionName:
<ImageView android:id="@+id/detail_image" android:layout_width="120dp" android:layout_height="120dp" android:transitionName="@string/image_transition"/> |
c. 启动activity:
/** * 使用共享元素启动activity * @param activity 从哪个页面跳转 * @param person 需要传递的参数对象 * @param view 设置为共享元素的控件 * @param str 两个activity中共享元素的transitionName */ public static void startActivity(Activity activity,Person person,View view,String str) { Intent intent = new Intent(activity, DetailActivity.class); intent.putExtra("person",person); Pair<View,String> pair =Pair.create(view,str); activity.startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(activity, pair).toBundle()); } |
4.2 Fragment之间的切换
a. 设置进入和退出时的动画:
detailFragment.setSharedElementEnterTransition(new DetailTransition()); setExitTransition(new Fade()); detailFragment.setEnterTransition(new Fade()); detailFragment.setSharedElementReturnTransition(new DetailTransition()); |
b. 启动Fragment:
getActivity().getSupportFragmentManager().beginTransaction() .addSharedElement(holder.getImageView(), getResources().getString(R.string.image_transition)) .replace(R.id.main_cl_container, detailFragment) .addToBackStack(null) .commit(); |
c. 共享元素的实现原理:
当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的窗口才完全可见。
参考:http://codecloud.net/15307.html
http://www.jianshu.com/p/b72718bade45
http://www.jianshu.com/p/692284dc3646
http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0130/2384.html