Android Activity跳转动画 - overridePendingTransition用法及原理分析

overridePendingTransition()是在Activity类中实现的一个用来实现跳转动画的方式,也是最常使用的方法。

overridePendingTransition()

先看这个名字,这个方法名很长,由三个单词组成,override是重写,覆盖的意思,pending表示即将到来的,transtition表示过渡,转换,也就是过渡动画的意思。三个单词连起来的意思就是覆盖即将到来的跳转动画,也就是可以通过这个方法添加的跳转动画会覆盖掉即将到来的跳转动画效果。即将到来的这个词很有意思,它表示即使不使用overridePendingTransition(),也会存在Activity切换动画,这个切换动画可能来自于其他方式添加的跳转动画,也可以来自于系统自带的默认动画。事实上,即使不通过本文介绍的任何方法添加Activity跳转动画,Activity也会有默认的跳转动画,这个默认的跳转动画定义在Android主题中。在android:Theme主题中可以看到这样一句样式定义,正是这句为App中所有Activity添加了一个默认的切换动画。

<item name="windowAnimationStyle">@style/Animation.Activity</item>

不过需要注意的是,@style/Animation.Activity中定义的动画样式在不同的系统版本上会有所不同,此外部分Theme自定义了新的windowAnimationStyle,所以默认的Activity动画并非一定是@style/Animation.Activity中定义的样子。

overridePendingTransition()使用方法

Activity的切换动画从业务层面上来说可以分为两种,一种是Activity启动时的动画,一种是从Activity返回时的动画,它们都可以通过overridePendingTransition()来设置,要设置启动时的动画需要在执行startActivity()或startActivityForResult()之后调用overridePendingTransition(),要设置返回时的动画需要在finish()之后调用overridePendingTransition()。启动动画和返回动画是相互独立的,设置启动动画不会对返回动画产生影响,如果只在startActivity()或startActivityForResult()之后调用了overridePendingTransition(),没有在finish()的时候调用,则Activity返回的时候仍然是默认的动画效果,也可以在finish()的时候使用和启动时不同的动画效果。

由于startActivity()和startActivityForResult()本质上是一样的,为了表述方便,后文不再提到startActivityForResult(),所有使用startActivity()的地方都可以用startActivityForResult()。

overridePendingTransition()只能作用在通过startActivity()和finish()方式启动和结束Activity的场景下,其他情况下的Activity创建和退出是不会有任何效果的。例如通过recreate()重建Activity,在其后执行overridePendingTransition()是没有动画效果的。

overridePendingTransition()的调用时机

注释和官方文档中关于此方法调用时机都写到“Call immediately after one of the flavors of startActivity(Intent) or finish to specify an explicit transition animation to perform next.”,也就是说此方法需要在startActivity() 或者finish()方法之后立即调用。因此标准的写法应当是这样的。注释和官方文档中关于此方法调用时机都写到“Call immediately after one of the flavors of startActivity(Intent) or finish to specify an explicit transition animation to perform next.”,也就是说此方法需要在startActivity() 或者finish()方法之后立即调用。因此标准的写法应当是这样的。

在这里插入图片描述
在这里插入图片描述

对一个Android Activity来说,除了可以调用finish()来结束之外,它还可以被Android的返回键所结束。在按下返回键后,会执行到onBackPressed()方法,在onBackPressed()方法中会自动调用finish()方法结束Activity。所以在添加Activity返回动画时,除了要在所有调用finish()的地方调用overridePendingTransition()之外,还要重写onBackPressed()方法,在执行super.onBackPressed()之后调用overridePendingTransition()。由于onBackPressed()总是在最后一步执行finish(),所以如下代码和执行完finish()后调用overridePendingTransition()是等同的。

在这里插入图片描述

immediately的含义通常被理解为调用startActivity()或finish()之后的下一行代码就必须调用overridePendingTransition()才能让动画效果生效。但事实上overridePendingTransition()可以推迟到startActivity() 或finish()之后的某个地方执行。

对启动Activity来说,overridePendingTransition()通常可以放到被启动Activity的onCreate()中来执行,很多文章都说overridePendingTransition()只能在调用super.onCreate()之前执行,这大部分情况是正确的,但并不是说overridePendingTransition()放到super.onCreate()之后就一定没有效果,事实上甚至可以把overridePendingTransition()放到onResume()来执行,这在很多时候是可以有效果的。

对结束Activity来说,finish()之后的overridePendingTransition()执行时机要严格很多,首先并不能将overridePendingTransition()推迟到onDestroy()中执行,也不能在finish()和overridePendingTransition()之间执行一些耗时的代码,但增加一些简单的处理逻辑通常是不会有问题的。

为了避免在不同设备上的差异,减少意外的发生,应当按照官方要求在startActivity() 或finish()方法之后立即调用overridePendingTransition(),对启动Activity来说,最迟应当放到被启动的Activity的onCreate()方法中执行super.onCreate()之前,对结束Activity来说,只能在finish()之后和super.onBackPessed()之后执行。

如果在Activity还未显示完成就调用startActivity()启动另一个Activity,这时在startActivity()之后调用overridePendingTransition()来设置启动动画是不会有效果的,例如在onCreate()中调用startActivity()和overridePendingTransition()就不会有动画效果,这时连默认的动画效果都不会有,被启动的Activity会直接显示出来。

overridePendingTransition()方法参数解析

overridePendingTransition()方法的定义如下。

public void overridePendingTransition(int enterAnim, int exitAnim)

它包含两个参数,分别为enterAnim和exitAnim,它们都是定义在anim文件夹中的某个动画资源的id。overridePendingTransition()方法只有一个定义,没有其他重载的方法,也就是说overridePendingTransition()设置的动画效果只能通过anim资源的方式来定义,无法通过代码创建。

enterAnim用来设置即将进入的Activity的动画效果,而exitAnim用来设置即将退出的Activity的动画效果。当从A启动B时,A是退出的Activity,B是进入的Activity,当从B返回A时则正好相反,A是进入的Activity,B是退出的Activity。也就是说,从A启动B时,B的动画效果是enterAnim,A的动画效果是exitAnim,从B返回A时,B的动画效果是exitAnim,A的动画效果是enterAnim。

在Android系统自带了两组动画效果,分别为fade_in/fade_out和slide_in_left/slide_out_right。可以通过如下代码来使用。

overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);

overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);

设置有如下场景,有三个Activity,分别为A,B和C,A启动B,B启动了C,但B启动C的之后立刻调用了finish()结束了自己,代码如下。这里overridePendingTransition()既在startActivity()之后执行,又在finsh()之后执行,这时动画效果究竟会如何展现呢?

Intent intent = new Intent(B.this, C.class);
startActivity(intent);
finish();
overridePendingTransition(R.anim.anim_down_in, R.anim.anim_down_out);

回答这个问题关键还在于理解overridePendingTransition()本身和enterAnim和exitAnim这两个参数的意义。
在前面我们有提到,Activity的切换动画分为两种,一种是Activity启动时的动画,一种是从Activity返回时的动画,但对overridePendingTransition()来说,它其实并不区分这两种动画类型,无论在startActivity()之后还是在finish()之后执行,它的结果都是一致的,overridePendingTransition()只是为即将到来的Activity切换添加动画效果,也就是为即将进入的Activity执行enterAnim动画,为即将退出的Activity执行enterAnim动画。所以只需要知道上述场景下哪个是即将进入的Activity,哪个是即将退出的Activity即可。显然在上述场景下,C是即将进入的Activity,B是即将退出的Activity,所以动画效果就是C执行enterAnim,B执行exitAnim。

上述enterAnim和exitAnim的值都允许为0,如果enterAnim值为0,表示没有enter动画效果,如果exitAnim为0表示没有exit 动画效果。如果两者都为0,则既没有enter动画效果,也没有exit动画效果,也就是不会有切换动画。但如果两个参数只有一个为0,则实际显示情况会比较复杂。

这里仍以A和B两个Activity为例,首先看A启动B的时候,如果执行overridePendingTransition(0, R.anim.anim_down_out);,也就是enterAnim为0,它表示没有enter动画效果,这时B会直接显示出来,而exitAnim不为0,也就是说A会有一个down_out的动画效果,然而B的界面是覆盖在A之上的,那么显然即使A执行了down_out动画,也是无法看到这个效果的。所以这时从视觉上看是不会有任何动画效果的。我们知道Activity所依附的Window是存在背景的,正是这个背景使得我们无法透过当前Activity看到下层的Activity,那么如果将B设置为一个透明背景的Activity,是否意味着就可以看到A执行down_out的动画效果呢。实践结果告诉我们,如果B的背景是透明的,我们能够透过B看到A,但在B直接显示出来之后,A压根就没动,任何看不到任何动画效果。

再来看overridePendingTransition(R.anim.anim_down_in, 0);,由于exitAnim为0,所以A是没有动画效果的,而B会有一个down_in的动画效果,这时在视觉上看到的会是B从底部升起,但在B逐渐升起的过程中,上面尚未被覆盖的部分并不能看到A的内容,而是一大块的黑色。这时如果B的背景是透明的,则会发现在B升起的过程中没有了黑色的部分,而是能够看到A的内容了。

再来看Activity返回的时候,如果执行overridePendingTransition(0, R.anim.anim_down_out);,这时没有enterAnim效果,只有exitAnim,而B是那个退出的Activity,所以B会有一个动画效果逐渐往下退出,而在B退出的过程中我们也是看不到A的,能看到的只是上方的一大块黑色。这时如果B是透明的,则能看到A就一直显示在B的下方,一动不动。如果执行overridePendingTransition(R.anim.anim_down_in, 0);,我们会看到B直接不可见,A从下方升起,A升起过程中上面同样是黑色的背景。奇怪的是,如果这时将B背景设置为透明,并非黑色的背景被取代,而是整个动画过程不可见。

上述过程总结如下。

启动Activity返回Activity
enterAnim为0B背景不透明:没有动画效果
B背景透明:没有动画效果
B背景不透明:B有动画效果,A不可见
B背景透明:B有动画效果,A可见,A无动画效果
exitAnim为0B背景不透明:B有动画效果,A不可见
B背景透明:B有动画效果,A可见,A无动画效果
B背景不透明:A有动画效果,B不可见
B背景透明:没有动画效果

在之前关于overridePendingTransition()动画的介绍中,都认为overridePendingTransition()动画是以Activity作为对象的,进入的Activity执行enterAnim,退出的Activity执行exitAnim,但如果是这样,上述现象有几个地方会无法解释。

  1. 在启动Activity,enterAnim为0,B背景透明时为何看不到A执行exitAnim
  2. 在返回Activity,extiAnim为0,B背景透明时,为何看不到A执行enterAnim

为了理解上述现象,只能从源码去看overridePendingTransition()究竟是如何实现Activity切换动画的。跟踪了一下,无奈其实现实在是过于复杂,网上资料也几乎没有,所以这里只好对其原理做个推测了。

  1. overridePendingTransition()并没有对Activity实现动画,所有的动画都是在View层面执行的。
  2. overridePendingTransition()执行的动画始终都在B中,无论是从A启动B,还是从B返回A,A始终是不变的。当从A启动B时,A会将自身显示做一个快照,将这个快照对象传递给B。从B返回A时,所谓A的进入动画,仍然是使用之前A传过来的这个快照,并非使用A实时的显示。
  3. 在B中,A的退出和进入动画都是通过背景实现的,它会将A传过来的快照和黑色的背景拼接在一起,按照设定的动画效果来显示,所以如果将B的背景设置为透明,则整个背景变得不可见,所以不会有任何A的动画效果。这就解释了上述问题,当B背景透明时,是不会看到A执行extiAnim和enterAnim的。
  4. 在B中,B的进入和退出动画实际上是对BcontentView的动画效果
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页