Android中转场动画的实现与兼容性处理
前言
在 android 5.0 之前,我们已经有了 overridependingtransition()
方法来实现一些转场效果。然而,在 android 5.0 以后,转场效果更加炫酷。
比如下面的动画:
一、android l 中的转场动画
实现转场动画只需三步:
在 res/ 目录下创建 transition 文件夹,在该文件夹下定义界面转场动画和共享元素的动画。
在 res/value/style 文件中为每个 activity 指定转场动画的 style ,并在 androidmanifest.xml 文件中为每个 activity 设置对应的 android:theme。
在 activity 调用 startactivity()
切换动画前,使用 activityoptionscompat 来创建转场动画时的共享对象。
下面就来对这三步进行详细讲解。
二、定义转场动画
在 res/ 目录下创建了 transition 资源文件夹后,就可以在该文件夹下对每一种动画进行定义。
一般来说,对 activity 定义一个过渡动画可以写成下面的形式:
<explode xmlns:android="http://schemas.android.com/apk/res/android"> <targets> <target android:excludeid="@android:id/statusbarbackground"/> <target android:excludeid="@android:id/navigationbarbackground"/> </targets> </explode>
其中, 是动画效果的名称,android 5.0(api 级别 21)支持这些进入与退出转换:
1、分解(explode):从场景中心移入或移出视图。
2、滑动(slide):从场景边缘移入或移出视图。
3、淡入淡出(fade):通过调整透明度在场景中增添或移除视图。
而每一种动画效果,都有额外的属性。比如滑动 slide,可以使用 android:slideedge="top"
设置滑动的方向;淡入淡出(fade)可以使用 android:fadingmode="fade_in"
设置具体是淡入(fade_in)还是淡出(fade_out)等。
标签里面定义需要转场(或者不需要转场)的目标 id ,这个 id 可以使系统自带的,也可以是我们自己视图中的 view 的 id,每一个 id 需要单独在 标签中定义,android:targetid
表示目标id需要进行过渡转换的 view,而 android:excludeid
表示我们不需要该 id 的 view 进行过渡转场。上面的那段代码的意思是说,除了状态栏和导航栏以外所有的 view,都执行 explode 动画。
如果我们想要在同一个过渡状态中实现两种或多种动画效果怎么办?也简单,将根标签替换为 ,然后定义每一种动画效果,最后记得在根标签中使用 android:transitionordering
注明这几种动画的演示顺序,sequential 表示顺序执行,而 together 表示同时执行。
比如像下面的代码:
<transitionset xmlns:android="http://schemas.android.com/apk/res/android"> <slide android:slideedge="bottom"> <targets> <target android:targetid="@id/cardview"/> </targets> </slide> <fade> <targets> <target android:excludeid="@android:id/statusbarbackground"/> <target android:excludeid="@android:id/navigationbarbackground"/> <target android:excludeid="@id/cardview"/> </targets> </fade> </transitionset>
这段代码的意思就很简单了,该 xml 定义了两个过渡动画,并且同时执行。第一个动画是针对 id 为 cardview 的 view 进行滑动,第二个动画将除了状态栏、导航栏和 cardview 以外的 view,进行淡入淡出。
三、为每个 activity 定义转场样式
这里的每一种动画,指的是在进行界面跳转过渡时,两个界面的状态。比如对于 activity a 和 activity b 这两个界面,可能的状态如下:
1、界面 a 跳转至界面 b :这时界面 a 是退出(exit )过渡状态,而对应的界面b是进入(enter)过渡状态。
2、界面 b 返回到界面 a :这时界面 a 是重新进入(reenter)过渡,而对应的界面b则是返回(return)过渡。
一般来说,所有的 activity 过渡动画都可以定义成如下的形式:
<style name="baseapptheme" parent="android:theme.material"> <!-- 开启过渡效果 --> <item name="android:windowcontenttransitions">true</item> <!-- 指定界面进入/退出动画效果 --> <item name="android:windowentertransition">@transition/explode</item> <item name="android:windowexittransition">@transition/explode</item> <!-- 指定共享元素进入/退出的动画效果 --> <item name="android:windowsharedelemententertransition"> @transition/change_image_transform</item> <item name="android:windowsharedelementexittransition"> @transition/change_image_transform</item> </style>
当然,你可以不用写全,比如在我的 demo 中一个界面的转场动画文件如下:
<style name="apptheme.detail"> <item name="windowactionbar">false</item> <item name="android:windownotitle">true</item> <item name="android:windowtranslucentstatus">true</item> <item name="android:windowallowentertransitionoverlap">false</item> <item name="android:windowentertransition">@transition/detail_enter</item> </style>
四、调用 activityoptionscompat
转场动画是在两个界面的跳转返回时发生的,所以,当使用 intent 跳转界面时,需要调用 activityoptionscompat来指定动画的运行。
一般来说,调用 activityoptionscompat 的模板代码如下:
// 创建一个包含过渡动画信息的 activityoptions 对象 activityoptions options = activityoptions.makescenetransitionanimation(this, view, getstring(r.string.image_transition_name)); // 使用 intent 跳转界面,并传递共享对象信息 intent intent = new intent(this, detailactivity.class); startactivity(intent, optionscompat.tobundle());
activityoptionscompat 是在support v4 包里面的,其实它是 activityoptions 的一个兼容(activityoptions是api 16引入的)。
然后,我们需要在第二个 activity 中,将转场的图片获取并显示到界面中。
多个共享元素的过渡实现
有时候我们需要让多个元素产生动画效果,可以使用 pair<> 来实现:
activityoptions options = activityoptions.makescenetransitionanimation(this, pair.create(view1, "agreedname1"), pair.create(view2, "agreedname2"));
五、手动实现一个转场动画
现在市面上,android 5.0 以下的手机系统还有一定的市场份额,所以为了照顾这些用户,我们只能手动实现一下共享元素的转场动画效果。
实现的思路也比较简单,大概的步骤如下:
1、确定第一个界面的共享元素,将其信息传递个第二个界面
2、第二个界面接收信息,开始的时候将界面设置为透明,并只显示共享元素。
3、将第二个界面的共享元素进行动画处理。
那么我们开始一步步实现上面的步骤。
获取共享元素位置信息
在第一个界面中,我们需要获取到共享元素的位置信息,并将其传递给下一个界面。于是乎,我们可以在第一个界面元素点击事件中,这么写:
public void imageclick(view view) { intent intent = new intent(animeactivity.this, animedetailactivity.class); // 创建一个 rect 对象来存储共享元素位置信息 rect rect = new rect(); // 获取元素位置信息 view.getglobalvisiblerect(rect); // 将位置信息附加到 intent 上 intent.setsourcebounds(rect); customimage customimage = (customimage) view; intent.putextra(animedetailactivity.extra_image, customimage.getimageid()); startactivity(intent); // 屏蔽 activity 默认转场效果 overridependingtransition(0, 0); }
其中,getglobalvisiblerect()
方法的含义是,获取 可见的状态栏高度+可见的标题栏高度+rect左上角到标题栏底部的距离,如果标题栏被隐藏了,那么可见标题栏高度为0。
接下来,就在在第二个界面接收位置信息并将该图片展示出来了。
模拟转场动画
在第二个界面中,我们需要做如下的操作:
1、获取上共享元素信息。
2、计算共享元素缩放比例和位移距离。
3、调用动画,完成模拟转场效果。
我将上面三个步骤的代码如下。
private void initial() { // 获取上一个界面传入的信息 mrect = getintent().getsourcebounds(); mrescourceid = getintent().getextras().getint(extra_image); // 获取上一个界面中,图片的宽度和高度 moriginwidth = mrect.right - mrect.left; moriginheight = mrect.bottom - mrect.top; // 设置 imageview 的位置,使其和上一个界面中图片的位置重合 framelayout.layoutparams params = new framelayout.layoutparams(moriginwidth, moriginheight); params.setmargins(mrect.left, mrect.top - getstatusbarheight(), mrect.right, mrect.bottom); mimageview.setlayoutparams(params); // 设置 imageview 的图片和缩放类型 mimageview.setimageresource(mrescourceid); mimageview.setscaletype(imageview.scaletype.center_crop); // 根据上一个界面传入的图片资源 id,获取图片的 bitmap 对象。 bitmapdrawable bitmapdrawable = (bitmapdrawable) getresources().getdrawable(mrescourceid); bitmap bitmap = bitmapdrawable.getbitmap(); // 计算图片缩放比例和位移距离 getbundleinfo(bitmap); // 创建一个 pallette 对象 mimagepalette = palette.from(bitmap).generate(); // 使用 palette 设置背景颜色 mcontainer.setbackgroundcolor( mimagepalette.getvibrantcolor(contextcompat.getcolor(this, android.r.color.black))); }
在12行,通过设置 margin 的形式来确定图片的位置,需要注意的是,由于状态栏是在父控件 framlayout 之外的,因此我们要将 rect.top 的值减去状态栏的高度,这样才是相对于屏幕的绝对位置。然后,getbundleinfo()
方法的代码如下:
private void getbundleinfo(bitmap bitmap) { // 计算图片缩放比例,并存储在 bundle 中 if (bitmap.getwidth() >= bitmap.getheight()) { mscalebundle.putfloat(scale_width, (float) mscreenwidth / moriginwidth); mscalebundle.putfloat(scale_height, (float) bitmap.getheight() / moriginheight); } else { mscalebundle.putfloat(scale_width, (float) bitmap.getwidth() / moriginwidth); mscalebundle.putfloat(scale_height, (float) mscreenheight / moriginheight); } // 计算位移距离,并将数据存储到 bundle 中 mtransitionbundle.putfloat(transition_x, mscreenwidth / 2 - (mrect.left + (mrect.right - mrect.left) / 2)); mtransitionbundle.putfloat(transition_y, mscreenheight / 2 - (mrect.top + (mrect.bottom - mrect.top) / 2)); }
动画处理
最后我们需要使用动画来模拟转场效果,代码如下:
private void runenteranim() { mimageview.animate() .setinterpolator(default_interpolator) .setduration(duration) .scalex(mscalebundle.getfloat(scale_width)) .scaley(mscalebundle.getfloat(scale_height)) .translationx(mtransitionbundle.getfloat(transition_x)) .translationy(mtransitionbundle.getfloat(transition_y)) .start(); }
很简单,自此,入场动画效果基本模拟完毕。
而退场动画就更简单了,直接上代码:
private void runexitanim() { mimageview.animate() .setinterpolator(default_interpolator) .setduration(duration) .scalex(1) .scaley(1) .translationx(0) .translationy(0) .withendaction(new runnable() { @override public void run() { finish(); overridependingtransition(0, 0); } }) .start(); }
总结
以上就是android中实现转场动画的全部内容,所以,是不是很简单?希望这篇文章的内容对各位android开发者们能有所帮助,如果有疑问大家可以留言交流。
推荐阅读
-
Android中转场动画的实现与兼容性处理
-
Android中ViewPager实现滑动条及与Fragment结合的实例教程
-
Android中转场动画的实现与兼容性处理
-
Android UI设计系列之自定义SwitchButton开关实现类似IOS中UISwitch的动画效果(2)
-
ThinkPHP5实现作业管理系统中处理学生未交作业与已交作业信息的方法
-
Android中ViewPager实现滑动条及与Fragment结合的实例教程
-
Android UI设计系列之自定义SwitchButton开关实现类似IOS中UISwitch的动画效果(2)
-
Android中WebView与Js交互的实现方法
-
Android中ViewPager实现滑动指示条及与Fragment的配合
-
Android中fragment与activity之间的交互(两种实现方式)