一、简介
布局动画视作特殊场景下的动画。在ViewGroup的布局发生变换时提供的过渡功能。
二、基于补间动画
ViewGroup首次加载时完成布局的动画。由于使用的是补间动画,因此在设置后,控件位置依旧不变。并且只在ViewGroup第一次layout的时候播放。
在使用补间动画会创建动画方式(移动,透明、缩放、旋转)并写好配置文件(xml或代码),之后调用,start即可。
xml
此处也很像,调用android:layoutAnimation
来设置布局动画资源给我们的ViewGroup控件。而layoutAnimation则会调用原始的补间动画来进行二次封装。
布局文件:<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layoutAnimation="@anim/layout_anim">
LayoutAnimation文件:<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/tran">
</layoutAnimation>
补间动画文件:<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="30"
android:duration="1000"
android:fillAfter="true">
</translate>
熟悉补间动画对上述不难理解了。中间只是封装了一层,当然他还有一下属性:
- interpolator:插值器
- delay:延迟每个子View动画起点的比例值
如:delay = 1,则每个子View的动画延迟都是delay *duration - animationOrder:子View动画的执行顺序,包括normal、reverse(倒置)、random(随机)
代码
以上xml方式也可以使用代码来实现。使用LayoutAnimationController
类来完成。最后在View.setLayoutAnimation
来设置该配置。
|
该子类GridLayoutAnimationController
是专门针对GridLayout的布局动画。
三、基于属性动画
不同与上述的效果,该类即用于被控制的View,也可以作用到其他的子类上(增删改)。
提供了以下几类:
|名称 | 说明|
| ——— | —— |
|APPEARING|View的添加或显示,作用在该新View上|
|DISAPPEARING|View的删除或隐藏,作用在该旧View上|
|CHANGE_APPEARING|View的添加或显示,作用在该其他View上|
|CHANGE_DISAPPEARING|View的删除或隐藏,作用在该其他View上|
|CHANGING|由于布局更改而更改,作用于所有View上,不是由添加到容器中或从容器中删除的项目引起的|
通过android:animateLayoutChanges="true"
来开启系统默认的过渡动画效果。
如果需要自定义动画,就需要上述的LayoutTransition
,但是需要注意,APPEARING、DISAPPEARING和CHANGE_XXXXX的使用方式不同,后者需要注意很多。
APPEARING & DISAPPEARING
代码如下:val layoutTransition = LayoutTransition()
// 加入(主)
val objectAnimator = ObjectAnimator.ofFloat(null, "translationX", 0f, 30f, 0f)
layoutTransition.setDuration(LayoutTransition.APPEARING, 300)
layoutTransition.setAnimator(LayoutTransition.APPEARING, objectAnimator)
// 推出(主)
val animatorSet = AnimatorSet()
val objectAnimator1 = ObjectAnimator.ofFloat(null, "scaleX",1f, 0f)
val objectAnimator2 = ObjectAnimator.ofFloat(null, "scaleY", 1f, 0f)
animatorSet.playTogether(objectAnimator1, objectAnimator2)
layoutTransition.setDuration(LayoutTransition.DISAPPEARING, 300)
layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, animatorSet)
lll.layoutTransition = layoutTransition
这里给lll
的layout控件添加了两种动画方式,方式也相对简单,将需要添加的动画添加至layoutTransition
并添加对应动画模式的时间即可。
CHANGE_XXXXX
这类动画模式,即添加或移除时的其他子控件动画效果。相对注意很多:
使用ObjectAnimator动画:
如果传入的是ObjectAnimator,就需要注意:
1.动画必须由PropertyValuesHolder组建
2.在组建动画时必须添加left、top、right、bottom中的两个(即使不需要这些功能)
3.组建的动画过程起始值必须和结束值相等
val pvhLeft = PropertyValuesHolder.ofInt("left", 0, 0)
val pvhTop = PropertyValuesHolder.ofInt("right", 0, 0)
val properViewHolder = PropertyValuesHolder.ofFloat("rotation", 0f, 360f, 0f)
val objectAnimator4 = ObjectAnimator.ofPropertyValuesHolder(this, properViewHolder, pvhLeft, pvhTop)
如上代码,必须符合上两点要求,否则动画无效。
使用AnimatorSet动画:
相对简单,注意点不多:
1.自动画可以由ObjectAnimator直接定义动画,再组合成AnimatorSet即可
2.组建的动画过程起始值必须和结束值相等
val animatorSet1 = AnimatorSet()
animatorSet1.playTogether(ObjectAnimator.ofFloat(null, "rotation", 0f, 360f, 0f))
CHANGING
指示由于布局更改而更改的项目上运行的动画,这些项目不是由添加到容器中或从容器中删除的项目引起的。
新增View是无法引起CHANGE动画的,只有改变布局大小才能引起动画效果。
CHANGE动画默认关闭,必须调用enableTransitionType(int)
方法开启
其他函数
函数名 | 功能 | |
---|---|---|
setDuration(long duration) | 设置此过渡对象的所有动画使用的持续时间 | 设置此过渡使用的其中一个动画对象的持续时间 |
setDuration(int transitionType, long duration) | 设置此过渡使用的其中一个动画对象的持续时间 | |
setInterpolator(int transitionType, TimeInterpolator interpolator) | 在此转换使用的动画对象之一上设置插补器 | |
setStagger(int transitionType, long duration) | 设置在其中一个更改动画期间开始每个动画之间延迟的时间长度 | |
setStartDelay(int transitionType, long delay) | 设置此转换使用的某个动画对象的启动延迟 |
四、Activity动画
overridePendingTransition
startActivity(android.content.intent)
或finish()
之后立即调用,以指定要执行next的显式转换动画。// 进入
startActivity(Intent(this, Main6Activity::class.java))
overridePendingTransition(R.anim.alpha_in_anim, R.anim.alpha_out_anim)
// 退出
finish()
overridePendingTransition(R.anim.alpha_in_anim, R.anim.alpha_out_anim)
传入的参数分别为:新Activity进入(旧Activity恢复)和旧Activity隐藏(新Activity隐藏)的两个参数。有点绕口,就是需要显示的Activity和需要隐藏的Activity的两个动画xml资源文件。
而Fragment可以使用transaction.setCustomAnimations(...
函数来添加动画。
注意:但是需要注意在设置进入动画xml,但是没哟设置退出动画,则会出现黑屏的情况overridePendingTransition(R.anim.alpha1_anim,0)
在调试会发现退出的Activity黑屏现象。可以设置一个不动的动画xml设置到退出参数即可。
可以使用AnimatorSet来完成组合动画效果
内置的Activity切换动画
进入 & 退出
提供了除“进入”和“退出”两种情景下的动画以外,还提供了一种共享元素动画
提供了如下功能:
enter:用于决定第一次打开当前Activity时的动画
exit : 用于决定退出当前Activity时的动画
reenter: 用于决定如果当前Activity已经打开过,并且再次打开该Activity时的动画
shared elements:用于决定在两个Activity之间切换时,指定两个Activity中对应的View的过渡效果
进入退出效果:
explode:从屏幕中间进入或退出
slide:从屏幕边缘进入或退出
fade:淡入淡出
如从ActivityA跳至ActivityB
ActivityB代码如下:override fun onCreate(savedInstanceState: Bundle?) {
// 提示切换需要使用动画
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
// 加载动画
window.enterTransition = Explode()
window.enterTransition = Slide()
window.enterTransition = Fade()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
可以看到加载动画的函数为enterXXXXX
,表示用于第一次打开当前Activity时的动画。
Window.FEATURE_CONTENT_TRANSITIONS
Window.FEATURE_ACTIVITY_TRANSITIONS
ActivityA代码如下://打开MainActivity
startActivity(Intent(this, MainActivity::class.java), ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
最后使用该方式打开ActivityB。
或者还可以在Styles.xml文件中添加上述这些动画效果:<item name="android:windowExitTransition">@transition/explode</item>
<item name="android:windowEnterAnimation">@transition/explode</item>
<item name="android:windowReenterTransition">@transition/explode</item>
ActivityOptions & ActivityOptionsCompat
最后在使用了该函数来生成Bundle。他会完成Activity的切换动画,但是功能有限。ActivityOptionsCompat是ActivityOptions的兼容包。
makeCustomAnimation(Context context, int enterResId, int exitResId)
指定进入和退出的Activity动画
enterResId:进入动画
exitResId:退出动画
makeClipRevealAnimation (View source, int startX, int startY, int width, int height)
从一个控件的裁剪区域放大然后打开新的Activity
startX & startY:裁剪的起点
width & height:区域的宽高
makeScaleUpAnimation(View source, int startX, int startY, int width, int height)
放大一个view,然后显示新的Activity
view:指定的控件
startX & startY:指定的起始点
width & height:放大时的起始宽高
makeThumbnailScaleUpAnimation(View source, Bitmap thumbnail, int startX, int startY)
放大一张图片,然后打开activity
source:此缩略图的动画视图。这定义了startX和startY的坐标空间
thumbnail:将显示为动画的初始缩略图的位图
startX & startY:放大时的起始位置
makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName)
添加一个共享元素动画的元素
makeSceneTransitionAnimation(Activity activity, Pair\ sharedElements…)
添加共享元素动画的多个元素,与上述相同
自定义效果
上述都使用java自带的类来产生效果,当然也可以使用xml文件来给这些效果DIY。
需要声明的xml资源文件目录指定在xml/transition目录下。
xml文件配置
- explode
- slide
- android:slideEdge:边缘的位置
- fade
- android:fadingMode:显示还是隐藏
- transition
- android:duration:动画时长毫秒
- android:interpolator:动画使用的插值器
- android:matchOrder:过渡动画执行顺序
- android:startDelay:在过渡动画之前延迟时间
- transitionSet
- android:transitionOrdering:确认是同时运行,还是顺序运行动画
引用
|
这里只需要引用到xml文件即可。
共享元素
进入进出提供了三种效果,而在共享元素则提供了5种效果:
ChangeBounds:改变目标View的边界完成动画
ChangeClipBounds:裁剪目标View的边界
ChangeImageTransform:改变目标图片的大小和缩放比例
ChangeTransform:改变目标View的缩放比例和旋转角度
ChangeScroll:改变滑动位置
同样,共享元素也可以在xml中编写配置文件,并引用。
连接Activity的组件
在ActivityA和ActivityB中的关联组件中添加transitionName="..."
,相同的名字就会跳到同名下的组件。
startActivity
在开启Activity的时候,将上述的控件对象、设置的名称添加进去即可:startActivity(Intent, ActivityOptions.makeSceneTransitionAnimation(this, open_two, "shared").toBundle())
也可以添加多个控件:startActivity(Intent, ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(open_one, "shareds"), Pair.create(open_two, "shared")).toBundle())
finishAfterTransition
在回退Acitivity时调用finishAfterTransition()
来退出并显示回退动画。
自定义共享元素动画
使用自定义元素路线来完成自定义共享元素动画。需要一个自定义的路径类:如PatternPathMotion
、ArcMotion
等。
然后让组件在设定的路径上完成动画即可。ArcMotion arcMotion = new ArcMotion();
// 用于描述两点之间的弧的最小角度
arcMotion.setMinimumHorizontalAngle(50f);
arcMotion.setMinimumVerticalAngle(50f);
然后在使用上述的效果ChangeBounds,即可:// 新建动画效果
val myChangsBounds = ChangeBounds()
// 新建曲线路径
val arcMotion = ArcMotion()
// 设置最小弧
arcMotion.minimumHorizontalAngle = 50f
arcMotion.minimumVerticalAngle = 50f
myChangsBounds.pathMotion = arcMotion
window.sharedElementEnterTransition = myChangsBounds
super.onCreate(savedInstanceState)
TransitionManager
运用Transition框架来完成更完美的场景切换。使用Scene(场景)来创建起始场景和结束场景,TransitionManager负责执行动画任务。
Scene
场景,即保存了View集合的一个状态,从起始到结束即从一个状态到另一个状态。
如何创建Scene:
- 构造函数Scene
Scene(ViewGroup sceneRoot, View layout)
sceneRoot:进入场景的根节点
layout:指定的一个场景状态(view布局)
如:val view = LayoutInflater.from(this).inflate(R.layout.layout_two, null, false)
scene2 = Scene(linearlayoutView, view)
即将状态view插入到linearlayoutView根节点中
- getSceneForLayout()
Scene.getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context)
sceneRoot:进入场景的根节点
layoutId:指定的一个状态
context:上下文
TransitionManager
TransitionManager用于场景变换时的切换功能,切换的方式默认使用AutoTransition
。
开启动画
- beginDelayedTransition()
当调用方法后,会保存当前根节点下的视图场景,在修改View的属性后,在自动会根据View的变换自动开启动画。beginDelayedTransition(ViewGroup sceneRoot, Transition transition)
beginDelayedTransition(ViewGroup sceneRoot)sceneRoot:场景的根节点
transition:动画方式(默认AutoTransition)
如:TransitionManager.beginDelayedTransition(layoutView);
if (flag) {
FrameLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams();
layoutParams.height =200;
layoutParams.width =200;
imageView.setLayoutParams(layoutParams);
} else {
FrameLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams();
layoutParams.height = 700;
layoutParams.width = 200;
imageView.setLayoutParams(layoutParams);
}
flag = !flag;
- go()
指定一个结束的Scene场景,来完成动画效果。go(Scene scene, Transition transition)
go(Scene scene)scene:指定的结束场景
transition:动画效果(默认AutoTransition)
Transition效果
以下效果:
Explode,Fade,Slide:根据View的显示和隐藏完成爆炸、透明、滑动效果。
ChangeBounds:改变目标View的边界完成动画
ChangeClipBounds:裁剪目标View的边界
ChangeImageTransform:改变目标图片的大小和缩放比例
ChangeTransform:改变目标View的缩放比例和旋转角度
ChangeScroll:改变滑动位置
也可以使用上述的transitionSet
在xml文件(res/transition)中完成混合动画效果。transition = TransitionInflater.from(this).inflateTransition(R.transition.test_transition);
TransitionManager.go(scene1, transition);
注意:在编写xml文件的时候,上述效果中可以指定layout中的view<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeBounds />
<fade android:fadingMode="fade_in_out">
<targets>
<target android:targetId="@id/transition_title" />
</targets>
</fade>
</transitionSet>
tragetId:指定该id的View完成动画
excludeId:指定除了该id以外的View完成动画
以上在java代码中也可以用实现
只对单纯View完成动画
使用Transition的addTarget()
和excludeTarget()
来完成上述中的功能,即指定Viewid和指定除了Viewid的其他View。如果同时调用两者,则调用excludeTarget()会从addTarget()中的View查找并移除。
四、Circular Reveal
material design设计使App的交互方式更丰富。官方将这一动画称为揭露效果。
可以显示或隐藏一组UI,使过程更加的连续。
ViewAnimationUtils.createCircularReveal()
ViewAnimationUtils.createCircularReveal()
为功能的构造函数,返回一个Anmation对象。
形参:
view:动画的区域
centerX:起始点x坐标
centerY:起始点y坐标
startRadius:起始半径
endRadius:结束半径