1 概述
视图动画只能作用于view,改变的是view的绘制效果,而不是view的属性。视图动画放置在anim中。
属性动画改变的是属性。属性动画放在animator目录下。
1.1属性动画的原理
属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据你传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。
你对object的属性xxx做动画,如果想让动画生效,要同时满足两个条件(必须同时满足):
1. object必须要提供setXxx方法,如果动画的时候没有传递初始值,那么还要提供getXxx方法,因为系统要去拿xxx属性的初始值(如果这条不满足,程序直接Crash)
2. object的setXxx对属性xxx所做的改变必须能够通过某种方法反映出来,比如会带来ui的改变啥的(如果这条不满足,动画无效果但不会Crash)
1.2 实现属性动画方式
- 给你的对象加上get和set方法,如果你有权限的话
- 用一个类来包装原始对象,间接为其提供get和set方法
- 采用ValueAnimator,监听动画过程,自己实现属性的改变
2 ValueAnimator
ValueAnimator是基本的动画类,处理值改动,通过监听某一值的变化,进行相应的操作。valueAnimator本身只处理值改动,而不关联到具体的对象。
2.1 对应的xml为animator,animator标签属性说明:
android:duration 动画从开始到结束持续的时长,单位为毫秒
android:startOffset 设置动画执行之前的等待时长,单位为毫秒
android:repeatCount 设置动画重复执行的次数
默认为0,即不重复;可设为-1或infinite,表示无限重复
android:repeatMode 设置动画重复执行的模式,可设为以下两个值其中之一:
restart 动画重复执行时从起点开始,默认为该值
reverse 动画会反方向执行
android:valueFrom 动画开始的值,可以为int值、float值或color值
android:valueTo 动画结束的值,可以为int值、float值或color值
android:valueType 动画值类型,若为color值,则无需设置该属性
intType 指定动画值,即以上两个value属性的值为整型
floatType 指定动画值为浮点型,默认值
colorType 指定动画值为颜色值,ARGB格式
pathType
android:interpolator 设置动画速率的变化,如加速,需指定Interpolator资源,见后面分析。
2.2 ValueAnimator类常用方法说明
setDuration(long duration) 设置动画总共的持续时间,以毫秒为单位。
start() 启动动画。若调用setStartDelay方法设置了动画延迟时间,那么会延迟运行动画;
调用start()方法后,isStarted()返回true;
动画真正运行起来后,isRunning()返回true,这时动画才会调用TimeInterpolator开始计算属性在某个时刻的值。
调用动画的start()方法所在的线程必须绑定了一个Looper对象,如果没有绑定就会报错。
如果我们想在一个View上使用属性动画,那么我们应该保证我们是在UI线程上调用的动画的start()方法。
start()方法运行后会触发动画监听器AnimatorListener的onAnimationStart方法的执行。
这里需要注意,start是一个异步调用。
setStartDelay(long startDelay) 设置动画的延迟运行时间
在调用了start()方法之后,isStarted()方法返回true,表示动画已经启动了
在start()方法调用后startDelay时间内,isRunning()返回false,表示动画还未真正运行
通过调用getStartDelay()方法可以返回我们设置的动画延迟启动时间,默认值是0。
setInterpolator(TimeInterpolator value) 改变动画所使用的时间插值器
通过动画的getInterpolator方法可以获取我们设置的时间插值器。
setTarget(Object target) 设置其要操作的对象,这样可以更新该对象的某个属性值。
对于ValueAnimator作用不大,因为ValueAnimator不是直接与某个对象打交道的。
对于ObjectAnimator作用较大,因为ObjectAnimator需要绑定某个要操作的对象。
pause() 停动画的执行。API19+支持
调用pause()方法的线程必须与调用start()方法的线程是同一个线程。
动画还没有执行start()或动画已结束,那么调用pause()方法没有任何影响,直接被忽略。
当执行了pause()方法后,动画的isPaused()方法会返回true。
pause()运行后会触发监听器AnimatorPauseListener的onAnimationPause方法的执行。
resume() 让动画从上次暂停的地方继续运行,API19+。
调用resume()方法的线程必须与调用start()方法的线程是同一个线程。
如果动画没有处于暂停状态(即isPaused()返回false),那么调用resume()方法会被忽略。
resume()运行后会触发监听器AnimatorPauseListener的onAnimationResume方法的执行。
end 动画会结束运行,直接从当前状态跳转到最终的完成状态,并将属性值分配成动画的终止值。
会触发动画监听器AnimatorListener的onAnimationEnd方法的执行。
cancel() 动画也会结束运行,但与end方法不同:不会将最终值分配给属性,而是保持当前改动的值。
会先触发AnimatorListener的onAnimationCancel执行,然后触发onAnimationEnd执行。
clone() 默认实现的只是浅拷贝,子类可以重写该方法以实现深拷贝。
2.3 PropertyValuesHolder
PropertyValuesHolder 是某一个属性值持有者,持有属性名,属性开始与结束值。他对应于animator的子标签propertyValuesHolder。PropertyValuesHolder对象可以用来创建与ValueAnimator或ObjectAnimator动画,上并联几个不同的属性进行操作。
PropertyValuesHolder 只能实现几个动画同时指向,要先后顺序执行的话,必须使用AnimatorSet
PropertyValuesHolder的工厂方法里面,包含整形ofInt()、浮点型ofFloat()、Object类型ofObject(),ofKeyframe(),下面介绍此类型。
属性设置方法为setIntValues/setFloatValues/setObjectValues/setKeyframe/setEvaluator
2.4 KeyFrame
Keyframe类型对象由一个time/value对组成,定义了指定时间点的指定值。
每一个keyframe还可以拥有自己的interpolator,控制了前一个关键帧到这一个关键帧之间的时间动画行为。
Keyframe 对象的构造也用是工厂方法:ofInt(), ofFloat(), or ofObject()。
Keyframe对象构造完之后就可以用 ofKeyframe()工厂方法来构造PropertyValuesHolder对象。
参数说明
Keyframe ofFloat(float fraction, float value)
fraction 动画完成度, value动画值
设置动画完成度为某一时刻的动画值。
KeyFrame需要配合PropertyValuesHolder.ofKeyframe来完成。
2.5 使用
使用以下方法初始化一个valueAnimator动画,并指定值变化:
ValueAnimator.ofXxx(xxx ...);
其中,参数为可变参数,可以为1到n个值,为n个值的时候,表示状态变化为从第1个值到第2个值, … , 一直到第n个值。还可以使用以下方法指定自己的Evaluator:
ValueAnimator.ofObject()
valueAnimator可以通过添加AnimatorUpdateListener监听值的变化,从而手动更新目标对象的属性。
2.6 ValueAnimator 的调用流程
a. 初始化ValueAnimator后,设置一个values。这时就有了一个PropertyViewHolder对象pvh。
可以直接调用setValues设置PVH对象;或setInt|Float|ObjectValues方法内部会生成一个PVH
PVH内部维护一个KeyframeSet和TypeEvaluator。PVH根据不同的values来初始化KeyframeSet
和TypeEvaluator.实现方法中的startValue和endValue就从KeyframeSet中的Keyframe中获取
b. 设置TypeEvaluator,传递到pvh中。
c. 设置Interpolator:ValueAnimator默认插入器是AccelerateDecelerateInterpolator
d. ValueAnimator的animationFrame(long currentTime)
当有动画应该结束时返回true,否则返回false。
方法内,算出动画运行的时间比率fraction,再调用animateValue(float fraction)。
e. ValueAnimator的animateValue(float fraction)
调用插入器,得到一个按某一规则得到的fraction
再调用 pvh.calculateValue(fraction);
pvh调用KeyframeSet的getValue(fraction);
KeyframeSet内部再调用TypeEvaluator的evaluate(fraction,T startValue, T endValue)。
startValue、endValue是通过Keyframe的getValue()来获取的。
f. evaluate(),方法内拿到了时间比率fraction,可以自行根据一定规则,返回value T。
3 ObjectAnimator
ObjectAnimator是ValueAnimator的子类,处理对象动画。对应的xml为objectAnimator,其可以直接指定动画的目标对象的属性。
objectAnimator标签属性除了包含animator标签的所有属性,还增加了以下属性:
android:propertyName 指定目标对象的属性名,
要求目标对象提供该属性的setter方法,若未设置动画初始值,还需要指定getter方法。
使用示例:
ObjectAnimator.ofXxx()
View中提供了getter和setter方法以便于实现动画的属性:
alpha:透明度,默认为1,表示不透明,0表示完全透明.
pivotX 和 pivotY:旋转的轴点和缩放的基准点,默认是View的中心点.
scaleX 和 scaleY:基于pivotX和pivotY的缩放,1表示无缩放,小于1表示收缩,大于1则放大.
rotation/rotationX/rotationY:基于轴点(pivotX和pivotY)的旋转.
rotation为平面的旋转,rotationX和rotationY为立体的旋转.
translationX/translationY:View的屏幕位置坐标变化量,以layout容器的左上角为坐标原点.
x 和 y:View在父容器内的最终位置,是左上角坐标和偏移量(translationX/translationY)的和.
4 AnimatorSet
AnimatorSet是动画集合,可以组合多种动画。
对应的xml为set
标签有一个属性可以设置动画的时序关系:
android:ordering 设置动画的时序关系,取值可为以下两个值之一:
together 动画同时执行,默认值
sequentially 动画按顺序执行
可以通过标签是嵌套标签,实现有些动画同时执行,有些按顺序执行。
5 Interpolators
Interpolators给动画中的时间 函数定义了一个用于计算的具体的值。
系统提供了以下Interpolators
AccelerateDecelerateInterpolator先加速再减速
对应xml属性accelerate_decelerate_interpolator
AccelerateInterpolator 加速,对应xml属性accelerate_interpolator
AnticipateInterpolator 先向后,然后向前抛出(抛物运动)
AnticipateOvershootInterpolator 先向后,向前抛出并超过目标值,然后最终返回到目标值。
BounceInterpolator 在结束时反弹
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 减速,对应xml属性decelerate_interpolator
LinearInterpolator 匀速线性变化
OvershootInterpolator 向前抛出,并超过目标值,然后再返回
TimeInterpolator 实现自定义插值的一个接口,用于自定义插值器。
xml中使用方法
android:interpolator="@android:anim/accelerate_interpolator"
java中使用方法
animator.setInterpolator(new AccelerateInterpolator());
6 Evaluators
Evaluators会告诉属性动画系统如何计算给定属性的值。它们利用Animator类提供时序数据:动画的开始和结束值,以及基于这些数据计算得来的属性动画值。
系统提供了以下Evaluators
IntEvaluator 用于计算int类型属性的值
FlaoatEvaluator 用于计算float类型属性的值
ArgbEvaluator 用于计算颜色属性的值,颜色属性值用十六进制表示。
RectEvaluator 用于计算矩形值
TypeEvaluator 计算类型值的泛型接口。
接口方法:evaluate(float fraction, T startValue, T endValue):用来计算任意类型值。
参数fraction表示时间的比率,即动画完成度。
startValue开始值,endValue结束值。
实现这个接口,在该方法中完成自己的计算类型值的方法。
7 监听器
7.1 addListener (Animator.AnimatorListener listener)
可以通过addListener方法向Animator添加动画监听器,该方法接收的是AnimatorListener接口类型的参数,其具有四个方法:onAnimationStart、onAnimationCancel、onAnimationEnd、onAnimationRepeat。Android中的AnimatorListenerAdapter类是个抽象类,其实现了AnimatorListener接口,并为所有方法提供了一个空实现。
7.2 addPauseListener (Animator.AnimatorPauseListener listener)
可以通过addPauseListener方法可以向Animator添加动画暂停相关的监听器,该方法接收的是AnimatorPauseListener接口类型的参数,具有两个方法:onAnimationPause和onAnimationResume。AnimatorListenerAdapter同样也实现了AnimatorPauseListener接口,并为所有方法提供了一个空实现。
7.3 addUpdateListener
监听动画变化事件, onAnimationUpdate方法会传递变化。
8 ViewPropertyAnimator
ViewPropertyAnimator是Android 3.1(API12+)新增功能。用于简化view的动画操作。
- 通过
View.animate()
方法获取一个ViewPropertyAnimator实例,之后的调用的所有方法,设置的所有属性都是通过这个实例完成的。 - ViewPropertyAnimator使用了隐式启动动画的功能,只要我们将动画定义完成之后,动画就会自动启动。并且这个机制对于组合动画也同样有效,只要我们不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动。
- 我们也可以显式地调用start()方法来启动动画,这是默认的隐式启动就会失效。
- ViewPropertyAnimator的所有接口都是使用连缀的语法来设计的,每个方法的返回值都是它自身的实例,因此调用完一个方法之后可以直接连缀调用它的另一个方法,这样把所有的功能都串接起来。
- 如果已在视图中设置了Listener,并打算在相同视图下,实现其他动画且不使用Listener函数,则需要将侦听器设为 null
其中,ViewPropertyAnimator方法说明如下:
alpha() 设定动画的 alpha 值;
scaleX()/scaleY() 将视图缩放于 X/Y 坐标轴;
translationZ() 在 Z 轴上平移视图;
setDuration() 设置动画时长;
setStartDelay() 设置动画延时;
setInterpolator() 设置动画插值;
setListener() 为动画的开始、结束、重复、取消设置侦听器。
9 加载xml动画
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.star);
animator.setTarget(view);
animator.start();
10 LayoutTransition(view动画)
ViewGroup中使用LayoutTransition进行监听布局的改变,而创建动画。简单说就是在ViewGroup中添加,删除,隐藏和显示View的时候,添加ViewGroup动画效果和View动画。
在ViewGroup的xml中添加android:animateLayoutChanges="true"
会启用LayoutTransition默认效果。
LayoutTransition定义了如下几种布局容器动画类型
APPEARING :当view出现或者添加的时候,view出现的动画
DISAPPEARING :当view消失或者隐藏的时候,view消失的动画
CHANGE_APPEARING :当添加view导致布局容器改变的时候,整个布局容器的动画
CHANGE_DISAPPEARING :当删除或者隐藏view导致布局容器改变的时候,整个布局容器的动画
用法示例:
ObjectAnimator animator = ObjectAnimator.ofFloat(null, "rotationY", 0, 90, 0);
LayoutTransition transition = new LayoutTransition();
parent.setLayoutTransition(layoutTransition);
transition.setAnimator(LayoutTransition.APPEARING, animator);
注: 需要将动画的target置为null
也可以通过xml设置动画效果,
参数解析:
android:delay 子类动画时间间隔(延时)
android:animationOrder 子类显现方式
random 表示随机
normal 默认
reserve 倒序
android:animation 表示子view显示的具体动画,可以指定某一个动画资源xml
示例:
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/left"
android:animationOrder="random"
android:delay="30%"/>
在Java代码中使用
Animation animation = AnimationUtils.loadAnimation(this,R.anim.left);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setOrder(LayoutAnimationController.ORDER_REVERSE);
controller.setDelay(11);
ViewGroup.setLayoutAnimation(controller);
11 Android 5.0新增动画
11.1 CircularReveal
使用剪切的圆形显示或隐藏一组UI元素。
ViewAnimatorUtils.createCircularReveal(View view,int centerX, int centerY, float startRadius, float endRadius)方法来创建一个附着在视图上的圆形,以显示或隐藏这个视图。
参数说明
view – 作用视图;
centerX –剪切圆的圆心位置X轴;
centerY -剪切圆的圆心位置Y轴;
startRadius –动画开始时剪切圆的半径。
endRadius - 动画结束时剪切园的半径。
使用示例:
int centerX = (startView.getLeft() + startView.getRight()) / 2;
int centerY = (startView.getTop() + startView.getBottom()) / 2;
float finalRadius = (float) Math.hypot((double) centerX, (double) centerY);
Animator mCircularReveal = ViewAnimationUtils.createCircularReveal(
targetView, centerX, centerY, 0, finalRadius);
11.2 波纹效果(Ripple)
当你使用了Material主题后,波纹动画会自动应用在所有的控件上。其对应RippleDrawable。可以通过如下代码设置波纹的背景:
android:background="?android:attr/selectableItemBackground" 波纹有边界
android:background="?android:attr/selectableItemBackgroundBorderless" 波纹超出边界
也可以通过设置xml属性来调节动画颜色,从而可以适应不同的主题:
android:colorControlHighlight:设置波纹颜色
android:colorAccent:设置checkbox等控件的选中颜色
12 Transition框架
Transition框架是Android4.4新加入的,作用于布局的动画,主要包含布局的添加、删除、移动、改变大小、显示、隐藏等动画。
Transition是负责捕获布局的差异,并产生动画切换UI状态的组件。整体过程有TransitionManager管理,一般执行步骤如下:
捕获启动状态
进行布局改变(场景转变或布局变化)
捕获结束状态
运行动画
该框架有3个核心元素:场景根;场景;转换。以下详细分析
12.1 TransitionManager
TransitionManager.beginDelayedTransition() 用以执行transition
private static Transition sDefaultTransition = new AutoTransition();
public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) {
if (Transition.DBG) {
Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
sceneRoot + ", " + transition);
}
sPendingTransitions.add(sceneRoot);
if (transition == null) {
transition = sDefaultTransition;
}
final Transition transitionClone = transition.clone();
sceneChangeSetup(sceneRoot, transitionClone);
Scene.setCurrentScene(sceneRoot, null);
sceneChangeRunTransition(sceneRoot, transitionClone);
}
}
首先会取出延迟的transition执行
然后获取transition实例,若未指定自己的transition则使用默认的静态AutoTransition实例。每次执行前都是clone一个transition实例(),以实现重用。
再通过captureValues监听view状态,若在transition中设置了目标viewId的启动状态,则捕获指定id的view,否则递归捕获所有视图启动状态。
12.2 获取TransitionManager方法:
首先,在xml中定义一个TransitionManager,比如:
<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
<transition android:fromScene="@layout/fragment_transition"
android:toScene="@layout/fragment_transition2"
android:transition="@transition/slow_auto_transition" />
<transition android:fromScene="@layout/fragment_transition2"
android:toScene="@layout/fragment_transition"
android:transition="@transition/slow_auto_transition" />
</transitionManager>
然后在Java中获取
mTransitionManager = transitionInflater.inflateTransitionManager(R.transition.transition_manager, container);
其中 container 为 场景运行所在父ViewGroup。
13 场景 Scene
场景根是一个普通视图组,其定义在UI各个层次如何转换,而场景则是一个特定的布局状态。
Scene定义了界面的当前状态信息.
从布局文件中载入Scene定义:
scene = Scene.getSceneForLayout(container, R.layout.example, context);
container Scene中一个包含了所有view的ViewGroup
用inflater载入现有的view来创建Scene
View view = inflater.inflate(R.layout.example, container, false);
Scene scene = new Scene(container, (ViewGroup)view);
14 场景切换
TransitionManager负责场景切换操作:
以前的切换动画( Activity#overridePendingTransition() 和FragmentTransaction#setCustomAnimation())只是针对整个activity而言,不够灵活。Transition使我们可以为每个 View 单独设置动画,甚至可以在两个独立的 Activity/Fragment 容器内共享某些 View的动画。
Transition 框架提供了两种类型的Activity过渡动画:
Content Transition 确定了非共享元素如何 进入/退出 Activity 场景
Shared Element Transition 确定了两个Activity共享View的动画效果。
切换场景方式:
使用自定义transition
mScene1 = Scene.getSceneForLayout(container, R.layout.fragment_transition, this);
mTransitionManager.transitionTo(mScene1); //方式一
mTransitionManager.go(mScene1, transition) //方式二
使用系统默认transition
mTransitionManager.go(mScene1); //方式一
mScene1.enter() //方式二,没有transition的情况
15 转换类型
默认情况可定制如下转换:
A 启动 B:
enter A进入的效果;
exit B退出的效果;
B 返回 A:(在代码中通过finishAfterTransition()方法触发返回动画)
return 返回,B进入的效果
reenter 返回时,A退出的效果
16 转换效果
普通Transition动画效果包含:
explode:从场景的中心移入或移出
slide:从场景的边缘移入或移出
fade:调整透明度产生渐变效果
Shared Elements Transition 共享元素转换:用于共享两个acitivity种共同的元素
changeBounds - 改变目标视图的布局边界,即对目标视图外边界进行动画
changeClipBounds - 裁剪目标视图边界,即对目标视图的附着物的外边界进行动画
changeTransform - 改变目标视图的缩放比例和旋转角度,即对目标视图进行缩放和旋转
changeImageTransform - 改变目标图片的大小和缩放比例,即对目标图片进行缩放
16.1 爆炸效果 Explode
Explode 转换允许视图从屏幕各个方位退出,会使压缩视图产生爆炸效果。在网格布局中爆炸效果尤其好。
实现方法:
- 需要在 res/transition 目录中创建如下转换
<explode xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"/>
- 将此设置为活动的转换。既可以将其添加到活动主题.
<style name="AppTheme.Explode" parent="AppTheme.NoActionBar">
<item name="android:windowExitTransition">@transition/slide_explode</item>
<item name="android:windowReenterTransition">@android:transition/slide_top</item>
</style>
或者使用Java代码实现
Transition explode = TransitionInflater.from(this).inflateTransition(R.transition.explode);
getWindow().setEnterTransition(explode);
16.2 滑动 slide
滑动切换可以使活动从屏幕右侧或底部滑入/出.滑动切换使我们依次滑动子视图.
向右侧滑示例:
<slide xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/decelerate_cubic"
android:slideEdge="end"/>
slideEdge 设置切换的的方向
start 左侧
end 右侧
bottom 底部
top 顶部
left 左侧
right 右侧
16.3 渐变 fade
渐变切换使活动转换出现淡入或淡出的效果。
<fade xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"/>
17 使用步骤
- 启用动画效果
方式一:先修改style文件,在继承了material主题的style.xml中添加如下属性
<style name="myTheme" parent="android:Theme.Material">
<!-- 允许使用transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- 指定进入和退出transitions -->
<item name="android:windowEnterTransition">@transition/explode</item>
<item name="android:windowExitTransition">@transition/explode</item>
<!-- 指定shared element transitions -->
<item name="android:windowSharedElementEnterTransition">
@transition/change_image_transform</item>
<item name="android:windowSharedElementExitTransition">
@transition/change_image_transform</item>
</style>
方式二:通过代码的方式启用
// 允许使用transitions
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
- 定义transition动画,xml中如下:
方式一:xml中定义
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<explode/>
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
</transitionSet>
方式二:可以通过如下方法在代码总设置transition效果:
Window.setEnterTransition():普通transition的进入效果
Window.setExitTransition():普通transition的退出效果
Window.setSharedElementEnterTransition():共享元素transition的进入效果
Window.setSharedElementExitTransition():共享元素transition的退出效果
- 启动Activity
通过ActivityOptions.makeSceneTransitionAnimation()方法启动一个新的Activity来激活这个Transition:
类别一:启用普通的Transition:
startActivity(intent,ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
类别二:启用共享元素Transition:
Intent intent = new Intent(this, Activity2.class);
// shareView: 需要共享的视图
// "shareName": 设置的android:transitionName="shareName"
ActivityOptions options = ActivityOptions
.makeSceneTransitionAnimation(this, shareView, "shareName");
startActivity(intent, options.toBundle());
如果有多个View需要共享,则通过Pair.create()方法创建多个匹配对然后传入ActivityOptions.makeSceneTransitionAnimation
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
Pair.create(view1, "agreedName1"),
Pair.create(view2, "agreedName2"));
如果不想使用transition可以设置options bundle为null。
注:当需要结束当前Activity并回退这个动画时调用Activity.finishAfterTransition()方法
18 优化转换
- 允许窗口页面转换——需要在主题中启用下列属性,主题都来源于一个资料主题:
<item name="android:windowContentTransitions">true</item>
- 启用/禁用转换重叠——上一转换过程结束,新的页面动画才会开始,这样就会形成时延。在不同的案例中,若启用如下属性,转换过程都会更加流畅自然:
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
- 排除特定视图转换—有时我们并不想让活动中的所有视图参与动画,而且大多数情况下,工具栏和状态栏是造成转换故障主因。所幸,可以排除特定的视图,使之无法转换:
<explode xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<targets>
<target android:excludeId="@android:id/navigationBarBackground"/>
<target android:excludeId="@android:id/statusBarBackground"/>
</targets>
</explode>
- 工具栏和操作栏——当使用操作栏的活动向使用工具栏的活动转换时(反之亦然),转换过程总是磕磕绊绊。为此,应当确保转换中的两个活动都使用相同的组件。
- 转换持续时间——既不能让用户等太久,也不能让动画转换过快。这取决于转换持续时间,最好通过试验敲定恰当的时间。笔者发现,多数情况下200-500微秒最为合适。
19 共享元素转换
共享元素转换方便我们为页面间的共享视图制作动画,使动画更为人性化,并给用户带来更好的视觉感受。
在布局中,必须使用 transitionName 属性将所有共享视图联系起来——这表明了视图间的转换关系。
- 声明共享转换名称,可以通过使用 XML 布局中的 transitionName 属性来完成。
以下为第一屏幕共享视图view的声明。
<RelativeLayout>
<LinearLayout>
<View
android:id="@+id/view_shared_transition"
android:transitionName="@string/transition_view"/>
<!-- Your other views -->
</LinearLayout>
</RelativeLayout>
以下为第二屏幕共享视图view的声明。
<LinearLayout>
<View
android:id="@+id/view_shared_transition"
android:transitionName="@string/transition_view"/>
<View
android:id="@+id/view_separator"/>
<TextView
android:id="@+id/text_detail"/>
<TextView
android:id="@+id/text_close"/>
</LinearLayout>
两个视图共享view_shared_transition这个view。
- 在页面1中创建 Pair 对象,使之包含转换视图与其 transitionName。然后将其传给页面选择实例(ActivityOptionsCompat),由此两个页面都得知了共享组件,就可以开始动画了
Pair participants = new Pair<>(mSquareView, ViewCompat.getTransitionName(mSquareView));
ActivityOptionsCompat transitionActivityOptions =
ActivityOptionsCompat.makeSceneTransitionAnimation(
SharedTransitionsActivity.this, participants);
ActivityCompat.startActivity(SharedTransitionsActivity.this,
intent, transitionActivityOptions.toBundle());
20 自定义转换
以下为一般步骤
首先创建一个SharedTransition,传入压缩视图与转换名称以引用共享组件。
然后创建ArcMotion 实例,使两个视图转换时形成曲线动画效果。
接下来扩展 ChangeBounds 以创建自定义转换,改变(morph)两个形状(对于button 和 FAB ,有两个不同的类)。此处重写了类中的多个方法,以便为所需属性做动画。最后,使用 ViewPropertyAnimator 调整对话框的透明度,使用 ObjectAnimator 调整两个视图间的色彩,使用 AnimatorSet 实例将两种动画效果整合在一起。
21 动态矢量图片
在 API 21+,AnimatedVectorDrawable 可用于制定VectorDrawable 属性的动画,生成动态图片。这部分可以参考drawable的AnimatedVectorDrawable部分和animated-vector部分
22 动画卡顿原因
使用 OneAPM 可以快速定位分析UI性能,Mobile Insight的卡顿可以直观地展示这些信息。
可以分析绘制APP卡顿趋势图,精确定位每1秒内的绘图刷新信号中断的次数,从多维度分析卡顿现象,如APP版本、操作系统版本的分布情况等。
卡顿详情列表展示:访问时间,发生卡顿时的流畅度,耗时,发生卡顿时的设备信息,APP版本,操作系统及版本,CPU信息
通过分析该页面信息可以清楚了解到卡顿来源,以便针对性快速优化。
动画卡顿的原因大概有这样三种,这些因素将直接影响动画的性能,导致卡顿。即:
手势滑动速度
帧率
触摸事件响应的速度
23 动画注意事项
- 当动画的设置了
android:repeatCount="infinite"
时,必须在当前动画view销毁时end动画,否则会造成资源无法释放。
24 本篇源码已经上传github