- 在两个
Activity
定义两个相同类型的View; - 给两个
View
设置相同的transitionName
属性; - 通过
ActivityOptions.makeSceneTransitionAnimation()
函数生成Bundle
对象; startActivity()
函数传递bundle
对象。
「栗子讲解,清晰易懂:」
- 分别在
activity_first.xml
和activity_second.xml
布局文件定义ImageView
组件,并将transitionName
属性设为activityTransform
。
`
<?xml version="1.0" encoding="utf-8"?>
`
「预览图」 activityTransform
属性也可以通过代码设置。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ivImage.transitionName="activityTransform" }
- 在
FirstActivity
中给ImageView
设置点击事件,跳转到第二个Activity。
ivImage.setOnClickListener { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//判断Android版本 val bundle = ActivityOptions.makeSceneTransitionAnimation(this, ivImage, "activityTransform") .toBundle() startActivity(Intent(this, SecondActivity::class.java), bundle) } else { startActivity(Intent(this, SecondActivity::class.java)) } }
代码中,先判断当前Android
版本是否大于等于5.0,大于或等于Android 5.0
的话就设置共享元素动画,小于5.0 就正常启动第二个Activity
。
通过ActivityOptions.makeSceneTransitionAnimation()
创建启动Activity
过渡的一些参数,makeSceneTransitionAnimation()
函数第一个参数为Activity
对象;第二个参数为共享元素组件,这里设置为id
是ivImage
的ImageView
视图;第三个参数为transitionName
属性的值,这里是activityTransform
。在调用AcivityOptions
对象toBundle
函数,包装成Bundle
对象。
「效果图:」
「多个共享元素过渡」
多个共享元素过渡也很简单,只需要调用makeSceneTransitionAnimation()
函数的另外一个重载函数即可。
- 在前面XML布局的基础上,给
TextView
增加transitionName
属性:textTransform
。
`
<?xml version="1.0" encoding="utf-8"?>
`
- 构建多个
Pair
对象,并传递给makeSceneTransitionAnimation()
函数,启动Activity
。
ivImage.setOnClickListener { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val imagePair=Pair<View,String>(ivImage,"activityTransform") val textPair=Pair<View,String>(ivImage,"textTransform") val bundle = ActivityOptions.makeSceneTransitionAnimation(this, imagePair,textPair).toBundle() startActivity(Intent(this, SecondActivity::class.java), bundle) } else { startActivity(Intent(this, SecondActivity::class.java)) } }
这里主要是通过将共享视图和transitionName
属性的值包装到Pair
对象,其他操作和一个共享元素的操作步骤并无区别。
「效果图:」
「深坑提醒」
有时从RecyclerView
界面进入到详情页,由于详情页加载延迟,可能出现没有效果。例如ImageView
从网络加载图片,可能A界面到B界面没效果,B回到A界面有效果。
解决步骤:
- 在
setContentView
后添加下面代码,延迟加载过渡动画。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { postponeEnterTransition() }
- 在共享元素视图加载完毕,或者图片加载完毕后调用下面代码,开始加载过渡动画。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { startPostponedEnterTransition() }
例如我是在Glide加载完再调用:
Glide.with(mContext) .asBitmap() .load(value?.avatar ?: "") .listener(object : RequestListener<Bitmap> { override fun onResourceReady(resource: Bitmap?, model: Any?, target: Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { animatorCallback?.invoke()//回调开始加载过渡动画 return false } override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Bitmap>?, isFirstResource: Boolean): Boolean { animatorCallback?.invoke()//回调开始加载过渡动画 return false } }) .apply(RequestOptions.circleCropTransform()) .placeholder(R.mipmap.ic_default) .error(R.mipmap.ic_default) .into(authorBinding!!.ivAvatar)
大家也可以考虑下面代码:
shareElement.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { override fun onPreDraw(): Boolean { shareElement!!.viewTreeObserver.removeOnPreDrawListener(this) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { animatorCallback?.invoke() } return true } })
二)、进入过渡与退出过渡动画
与共享元素相反的,就是Activity进入与退出过渡动画,两个Activity之间在没有共享的视图情况下进行动画切换。下面先看三种动画效果图:「爆炸式效果」和「淡入淡出式效果」、「滑动式效果」。
- 「爆炸式」:将视图移入场景中心或从中移出;
- 「滑动式」:将视图从场景的其中一个边缘移入或移出;
- 「爆炸式」:通过更改视图的不透明度,在场景中添加视图或从中移除视图;
第一个界面采用Fade
淡入淡出效果,第二个界面采用了Explode
爆炸效果。
前后两个界面都采用了Slide
滑入滑出效果。
利用Android现有的过渡框架,实现起来是很简单的,步骤如下:
- 在
Activity
的onCreate()
方法中调用setContentView()
前设置启用窗口过渡属性;
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
- 创建过渡效果对象
Slide
、Explode
、Fade
;
val slide=Slide() slide.slideEdge=Gravity.START slide.duration=300//效果时长,一般Activity切换时间很短,不建议设置过长
如果是Slide
效果,可以设置slideEdge
属性来指定滑动方向,默认是Gravity.BOTTOM
。
- 将过渡效果设置给window相关属性,设置;
//退出当前界面的过渡动画 window.exitTransition = slide //进入当前界面的过渡动画 window.enterTransition = slide //重新进入界面的过渡动画 window.reenterTransition = slide
- 调用第二个
Activity
界面,使用过渡效果。
startActivity( Intent(this, SecondActivity::class.java), ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
那么Activity
的OnCreate()
方法看起来是这样子的。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS) window.allowEnterTransitionOverlap=false Slide().apply { duration = 300 excludeTarget(android.R.id.statusBarBackground, true) excludeTarget(android.R.id.navigationBarBackground, true) }.also { window.exitTransition = it window.enterTransition = it window.reenterTransition = it } } setContentView(R.layout.activity_first) ivContent.setOnClickListener { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { startActivity( Intent(this, SecondActivity::class.java), ActivityOptions.makeSceneTransitionAnimation(this).toBundle() ) } } }
上面代码中调用 了excludeTarget()
方法将状态栏和导航栏排除在过渡动画效果之外。否则会跟着一起起动画效果,不是很美观。
正常情况,退出与进入过渡动画会有一小段交叉的过程,而window.allowEnterTransitionOverlap=false
就是禁止交叉,只有退出过渡动画结束后才会再显示进入过渡动画。
如果第二个Activity
在finish
掉后,回到第一个Activity
界面也想有过渡效果,就不要手动调用finish()
,可以调用finishAfterTransition ()
方法。
三)、兼容Android 5.0前
如果Android 5.0前也想要有切换动画怎么办?
- 在
res/anim
文件夹下创建想要的效果:
<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/decelerate_quad" android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="@android:integer/config_longAnimTime" />
- 在启动
Activity
后调用overridePendingTransition()
方法。
val intent = Intent(this, TestActivity2::class.java) startActivity(intent) overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
overridePendingTransition()
方法第一个参数为下一个界面进入动画,第二个参数为当前界面退出动画。
到这里,Activity
的切换使用过渡动画基本就结束了。有朋友可能会问,只有Activity切换才能应用过渡效果么?
二、布局变化过渡动画
在上一节要理解一个概念:场景。布局的显示与隐藏可以理解分别为一个场景,过渡动画就是解决场景切换带来的生硬视觉感受。Activity切换过渡动画指在两个Activity之间,而布局变化过渡动画,是指同个Activity之间View的变化过渡动画。
一)、手动创建Scene
手动创建场景的话,需要我们自己创建起始和结束场景,利用现有的过渡效果来达到两个场景的切换。默认情况下,当前界面就是起始场景。
- 创建起始场景和结束场景的
xml
布局。起始场景和结束场景需要有相同根元素,例如下面代码id
为flConatent
的FrameLayout
布局。
`<?xml version="1.0" encoding="utf-8"?>
最后
其实要轻松掌握很简单,要点就两个:
- 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
- 多练。 (视频优势是互动感强,容易集中注意力)
你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
阿里P7Android高级教程
下面资料部分截图,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。
附送高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、近期面试跳槽、自身职业规划迷茫的朋友们。
Android核心高级技术PDF资料,BAT大厂面试真题解析;
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
593520)]
附送高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、近期面试跳槽、自身职业规划迷茫的朋友们。
Android核心高级技术PDF资料,BAT大厂面试真题解析;
[外链图片转存中…(img-737k6NrC-1714459593520)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!