Android Style之属性动画

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 实现属性动画方式

  1. 给你的对象加上get和set方法,如果你有权限的话
  2. 用一个类来包装原始对象,间接为其提供get和set方法
  3. 采用ValueAnimator,监听动画过程,自己实现属性的改变

2 ValueAnimator

ValueAnimator是基本的动画类,处理值改动,通过监听某一值的变化,进行相应的操作。valueAnimator本身只处理值改动,而不关联到具体的对象。

2.1 对应的xml为animator,animator标签属性说明:

android:duration 动画从开始到结束持续的时长,单位为毫秒
android:startOffset 设置动画执行之前的等待时长,单位为毫秒
android:repeatCount 设置动画重复执行的次数
    默认为0,即不重复;可设为-1infinite,表示无限重复
android:repeatMode 设置动画重复执行的模式,可设为以下两个值其中之一:
    restart 动画重复执行时从起点开始,默认为该值
    reverse 动画会反方向执行
android:valueFrom 动画开始的值,可以为int值、float值或colorandroid:valueTo 动画结束的值,可以为int值、float值或colorandroid: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()方法运行后会触发动画监听器AnimatorListeneronAnimationStart方法的执行。
    这里需要注意,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()方法会返回truepause()运行后会触发监听器AnimatorPauseListeneronAnimationPause方法的执行。

resume() 让动画从上次暂停的地方继续运行,API19+。
    调用resume()方法的线程必须与调用start()方法的线程是同一个线程。
    如果动画没有处于暂停状态(即isPaused()返回false),那么调用resume()方法会被忽略。
    resume()运行后会触发监听器AnimatorPauseListeneronAnimationResume方法的执行。

end 动画会结束运行,直接从当前状态跳转到最终的完成状态,并将属性值分配成动画的终止值。
    会触发动画监听器AnimatorListeneronAnimationEnd方法的执行。

cancel() 动画也会结束运行,但与end方法不同:不会将最终值分配给属性,而是保持当前改动的值。
    会先触发AnimatorListeneronAnimationCancel执行,然后触发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的动画操作。

  1. 通过View.animate()方法获取一个ViewPropertyAnimator实例,之后的调用的所有方法,设置的所有属性都是通过这个实例完成的。
  2. ViewPropertyAnimator使用了隐式启动动画的功能,只要我们将动画定义完成之后,动画就会自动启动。并且这个机制对于组合动画也同样有效,只要我们不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动。
  3. 我们也可以显式地调用start()方法来启动动画,这是默认的隐式启动就会失效。
  4. ViewPropertyAnimator的所有接口都是使用连缀的语法来设计的,每个方法的返回值都是它自身的实例,因此调用完一个方法之后可以直接连缀调用它的另一个方法,这样把所有的功能都串接起来。
  5. 如果已在视图中设置了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 转换允许视图从屏幕各个方位退出,会使压缩视图产生爆炸效果。在网格布局中爆炸效果尤其好。

实现方法:

  1. 需要在 res/transition 目录中创建如下转换
<explode xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"/>
  1. 将此设置为活动的转换。既可以将其添加到活动主题.
<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 使用步骤

  1. 启用动画效果

方式一:先修改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); 
  1. 定义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的退出效果
  1. 启动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 优化转换

  1. 允许窗口页面转换——需要在主题中启用下列属性,主题都来源于一个资料主题:
<item name="android:windowContentTransitions">true</item>
  1. 启用/禁用转换重叠——上一转换过程结束,新的页面动画才会开始,这样就会形成时延。在不同的案例中,若启用如下属性,转换过程都会更加流畅自然:
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
  1. 排除特定视图转换—有时我们并不想让活动中的所有视图参与动画,而且大多数情况下,工具栏和状态栏是造成转换故障主因。所幸,可以排除特定的视图,使之无法转换:
<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>
  1. 工具栏和操作栏——当使用操作栏的活动向使用工具栏的活动转换时(反之亦然),转换过程总是磕磕绊绊。为此,应当确保转换中的两个活动都使用相同的组件。

  2. 转换持续时间——既不能让用户等太久,也不能让动画转换过快。这取决于转换持续时间,最好通过试验敲定恰当的时间。笔者发现,多数情况下200-500微秒最为合适。

19 共享元素转换

共享元素转换方便我们为页面间的共享视图制作动画,使动画更为人性化,并给用户带来更好的视觉感受。

在布局中,必须使用 transitionName 属性将所有共享视图联系起来——这表明了视图间的转换关系。

  1. 声明共享转换名称,可以通过使用 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. 在页面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 动画注意事项

  1. 当动画的设置了android:repeatCount="infinite"时,必须在当前动画view销毁时end动画,否则会造成资源无法释放。

24 本篇源码已经上传github

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值