大纲
Activity的overridePendingTransition方法
官方文档中说:
Call immediately after one of the flavors of startActivity(android.content.Intent) or finish() to specify an explicit transition animation to perform next.
文档指明overridePendingTransition必需在startActivity()或者finish()函数之后立即调用。
public void overridePendingTransition (int enterAnim, int exitAnim)
overridePendingTransition()方法接收的是两个动画资源enterAnim和exitAnim。
当通过startActivity()从A进入B时,B执行enterAnim,A执行exitAnim;
当通过finish()从B回退A时,A执行enterAnim,B执行exitAnim。
我们这里模仿微信的推进推出效果,创建4个过场动画文件:
(这里注意一个点就是android:duration设置最好一致,因为如果在startActivity的时候进入enterAnim大于exitAnim,就会出现新的Activity还没有完全进入界面,而上一个页面已经滑出了导致背景全黑)
activity_open_enter.xml 对应startActivity时的enterAnim
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100%"
android:toXDelta="0"
android:duration="300"/>
</set>
activity_open_exit.xml 对应startActivity时的exitAnim
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="0"
android:toXDelta="-100%" />
</set>
activity_close_enter 对应finish时的enterAnim
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="300"
android:fromXDelta="-100%"
android:toXDelta="0"/>
</set>
activity_close_exit 对应finish时的exitAnim
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="100%"
android:duration="300">
</translate>
</set>
我们在startActivity时
//MainActivity.kt
val intent = Intent(this@MainActivity, Step1Activity::class.java)
startActivity(intent)
overridePendingTransition( R.anim.activity_open_enter, R.anim.activity_open_exit)
finish时
//Step1Activity.kt
finish()
overridePendingTransition( R.anim.activity_close_enter, R.anim.activity_close_exit)
就实现了微信的推入推出的效果
如果不需要切换动画可以设置overridePendingTransition(0,0),注意,不要动画不是默认动画,这里不要动画是直接显示新的Activity没有任何动画效果
另外官方文档后半部分中又引出了新的东西:
As of Build.VERSION_CODES.JELLY_BEAN an alternative to using this with starting activities is to supply the desired animation information through a ActivityOptions bundle to startActivity(android.content.Intent, android.os.Bundle) or a related function. This allows you to specify a custom animation even when starting an activity from outside the context of the current top activity.
在安卓5.0之后可以创建一个携带一个动画设置的ActivityOptions,在startActivity(android.content.Intent, android.os.Bundle)时转为Bundle使用。
文档给我们引出了一个新的东西——ActivityOptions,关于ActivityOptions的详细使用,它提供了多种动画的方法,这里暂时不多做扩展,只挑它两个使用方法先说,而且放在最后说,因为我们要先说安卓提供的另外一种过场动画的设置方法。
theme设置windowAnimationStyle
安卓提供了一组style属性可以直接在manifest里面设置某个Activity的过场动画,当然也可以直接设置在Application的theme上从而全局配置,这里的优先级是小于java代码中动态设置的。
activityOpenEnterAnimation
activityOpenExitAnimation
activityCloseEnterAnimation
activityCloseExitAnimation
这些属性对应与上面两种场景的两对参数,属性的名称也更加易懂
这里就以设置给Application为例创建一个WindowAnimationStyle
<style name="WindowAnimationStyle" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/activity_open_enter</item>
<item name="android:activityOpenExitAnimation">@anim/activity_open_exit</item>
<item name="android:activityCloseEnterAnimation">@anim/activity_close_enter</item>
<item name="android:activityCloseExitAnimation">@anim/activity_close_exit</item>
</style>
这里我们新建了一个全屏的AppTheme作为Application的theme,将这个windowAnimationStyle设置给AppTheme
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@style/WindowAnimationStyle</item>
</style>
在Manifest中将AppTheme设置给Application
<application
......
android:theme="@style/AppTheme">
</application>
现在app中的所有页面基本都具有相同的转场动画了。为什么说基本上,因为这样设置后不同的Activity栈之间切换仍旧是默认的动画(如果这个时候跳转launchMode是singleTask或者使用Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK作为flag的,你会发现仍旧是默认的动画的,并且默认的跨任务栈的动画有些突兀)。谷歌提供了另外一组属性设置跨Activity栈的转场动画
taskOpenEnterAnimation
taskOpenExitAnimation
taskCloseEnterAnimation
taskCloseExitAnimation
我们把这几个属性在WindowAnimationStyle里面补齐,这样跨Activity栈的过场动画就也保持一致啦。
<style name="WindowAnimationStyle" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/activity_open_enter</item>
<item name="android:activityOpenExitAnimation">@anim/activity_open_exit</item>
<item name="android:activityCloseEnterAnimation">@anim/activity_close_enter</item>
<item name="android:activityCloseExitAnimation">@anim/activity_close_exit</item>
<item name="android:taskOpenEnterAnimation">@anim/activity_open_enter</item>
<item name="android:taskOpenExitAnimation">@anim/activity_open_exit</item>
<item name="android:taskCloseEnterAnimation">@anim/activity_close_enter</item>
<item name="android:taskCloseExitAnimation">@anim/activity_close_exit</item>
</style>
ActivityOptions
最后我们再来说下这个ActivityOptions。之所以放在最后说,是因为前两种比较相似,而ActivityOptions可以处理更复杂的效果,可以对过场的View等做操作,实现共享元素等更加炫酷的效果。
首先,这个ActivityOptions是完全可以替代overridePendingTransition,所以提供了一个类似的方法,接收两个动画资源
makeCustomAnimation(Context context,int enterResId, int exitResId)
我们在MainActivity中这样启动Step1Activity
val intent = Intent(this@MainActivity, Step1Activity::class.java)
val activityOptions = ActivityOptions.makeCustomAnimation(this, R.anim.activity_open_enter, R.anim.activity_open_exit)
startActivity(intent,activityOptions.toBundle())
这样就实现了跟上面一样的效果,但是我没有找到方法处理finish场景(在共享中倒是可以使用finishAfterTransition回退),如果你知道,欢迎在下方留言。
下面再说一下ActivityOptions实现共享元素,关于ActivityOptions其他的用法可以去查看官方文档。
ActivityOptions共享元素
关于什么是共享元素,这个不太好解释,我们直接看例子吧
图-1 | 图-2 |
---|---|
图-1的两个不同的Activity上显示logo的ImageView就是共享元素;图-2中的显示logo的View和另一个Activity上的back按钮也是共享元素,相信你已经明白什么叫共享元素了。
首先在跳转到的Step1Activity布局中为共享元素声明名称
android:transitionName="transition"
MainActivity跳转用到了ActivityOptions的makeSceneTransitionAnimation方法,参数说明我已经标注在注释上了
/**
* @param sharedElement当前Activity中共享元素View
* @param 跳转至页面共享元素名称
*/
makeSceneTransitionAnimation(Activity activity,View sharedElement, String sharedElementName)
当有多组共享元素时,还有另外一个方法
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,Pair<View, String>... sharedElements)
跳转使用下面的方法
val intent = Intent(this@MainActivity, Step1Activity::class.java)
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
this,
findViewById<ImageView>(R.id.iv_logo_top),
"transition"
)
startActivity(intent, activityOptions.toBundle())
回退时注意需要使用
finishAfterTransition()
才能在回退时也具有这个共享元素的动画
总结
一般我们可以通过theme的方法设置全局的Activity入场出厂动画,然后在一些需要特殊动画的地方使用ActivityOptions或overridePendingTransition处理特殊动画,因为代码中设置的动画的优先级要高,可以覆盖theme中设置的全局动画。
参考
Android动画之Activity切换动画overridePendingTransition实现和Theme Xml方式实现
Android页面切换动画(包括不同任务栈之间页面切换动画)通过Theme去设置