Android动画

1.为什么要说动画?

  • 动画的适用是Android开发常用的知识
  • 种类繁多,适用复杂,很多实现需要自定义动画

2.目前Android中有多少种动画?

  • 视图动画(View 动画)
  • 属性动画
  • 揭露动画(Reveal Effect)
  • 转场动画 & 共享元素(Activity 切换动画)
  • 矢量动画 过往分享
  • 约束布局实现的关键帧动画(ConstraintSet MotionLayout)王星分享内容
  • lottie-android动画 永叔分享内容

3.视图动画(View Animation)

  • 作用对象:视图(View)
  • 具体分类:补间动画 & 逐帧动画

3.1补间动画(Tween Animation)

Android动画_第1张图片
image.png

3.1.1作用对象

1.如Android的TextView、Button等等
2.不可作用于View组件的属性,如:颜色、背景、长度等等

3.1.2原理

通过确定开始的视图样式 & 结束的视图样式、中间动画变化过程由系统补全来确定一个动画

1.结束的视图样式:平移、缩放、旋转 & 透明度样式
2.即补间动画的动画效果就是:平移、缩放、旋转 & 透明度动画

3.1.3分类

Android动画_第2张图片
image.png

3.1.4具体使用

补间动画的使用方式分为两种:在XML 代码 / Java 代码里设置

1.前者优点:动画描述的可读性更好
2.后者优点:动画效果可动态创建

3.1.4.1平移动画

设置方法1:在XML 代码中设置

  • 步骤1:在 res/anim的文件夹里创建动画效果.xml文件

此处路径为res/anim/view_animation.xml

  • 步骤2:根据 不同动画效果的语法 设置 不同动画参数,从而实现动画效果。平移动画效果设置具体如下:
    view_animation.xml

// 采用 标签表示平移动画
 
  • 步骤3:在Java代码中创建Animation对象并播放动画
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button mButton = (Button) findViewById(R.id.Button);
        // 步骤1:创建 需要设置动画的 视图View
        Animation translateAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation);
        // 步骤2:创建 动画对象 并传入设置的动画效果xml文件
        mButton.startAnimation(translateAnimation);
        // 步骤3:播放动画

设置方法2:在 Java 代码中设置

  Button mButton = (Button) findViewById(R.id.Button);
        // 步骤1:创建 需要设置动画的 视图View

        Animation translateAnimation = new TranslateAnimation(0,500,0,500);
        // 步骤2:创建平移动画的对象:平移动画对应的Animation子类为TranslateAnimation
        // 参数分别是:
        // 1. fromXDelta :视图在水平方向x 移动的起始值
        // 2. toXDelta :视图在水平方向x 移动的结束值
        // 3. fromYDelta :视图在竖直方向y 移动的起始值
        // 4. toYDelta:视图在竖直方向y 移动的结束值

        translateAnimation.setDuration(3000);
        // 固定属性的设置都是在其属性前加“set”,如setDuration()
        mButton.startAnimation(translateAnimation);
        // 步骤3:播放动画

3.1.4.2缩放动画 类似平移,例子略

3.1.4.3旋转动画 类似平移,例子略

3.1.4.4透明动画 类似平移,例子略

3.1.4.5组合动画

  • 上面讲的都是单个动画效果;而实际中很多需求都需要同时使用平移、缩放、旋转 & 透明度4种动画,即组合动画
  • 使用组合动画需要用到标签 < Set/>

Set 对于 Animation,就类似 View 对于 ViewGroup

3.1.4.5.1在XML 代码中设置
  • 步骤1:在路径 res/anim 的文件夹里创建动画效果 .xml 文件

此处为res/anim/view_animation.xml

  • 步骤2:组合动画的设置方法 同 单个动画设置。具体如下:

// 采用< Set/>标签


// 组合动画同样具备公共属性
    android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
    android:startOffset ="1000" // 动画延迟开始时间(ms)
    android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
    android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
    android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
    android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
    android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
    android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲

// 组合动画独特的属性
    android:shareinterpolator = “true”
    // 表示组合动画中的动画是否和集合共享同一个差值器
    // 如果集合不指定插值器,那么子动画需要单独设置

// 组合动画播放时是全部动画同时开始
// 如果想不同动画不同时间开始就要使用android:startOffset属性来延迟单个动画播放时间

// 设置旋转动画,语法同单个动画
    

// 设置平移动画,语法同单个动画
    

// 设置透明度动画,语法同单个动画
    


// 设置缩放动画,语法同单个动画
    
// 特别注意:
// 1. 在组合动画里scale缩放动画设置的repeatCount(重复播放)和fillBefore(播放完后,视图是否会停留在动画开始的状态)是无效的。
// 2. 所以如果需要重复播放或者回到原位的话需要在set标签里设置
// 3. 但是由于此处rotate旋转动画里已设置repeatCount为infinite,所以动画不会结束,也就看不到重播和回复原位


  • 步骤3:在Java代码中创建Animation对象并播放动画
Button mButton = (Button) findViewById(R.id.Button);
        // 步骤1:创建 需要设置动画的 视图View
        Animation translateAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation);
        // 步骤2:创建 动画对象 并传入设置的动画效果xml文件
        mButton.startAnimation(translateAnimation);
        // 步骤3:播放动画
3.1.4.5.2在 Java 代码中设置
Button mButton = (Button) findViewById(R.id.Button);
        // 创建 需要设置动画的 视图View

        // 组合动画设置
        AnimationSet setAnimation = new AnimationSet(true);
        // 步骤1:创建组合动画对象(设置为true)


        // 步骤2:设置组合动画的属性
        // 特别说明以下情况
        // 因为在下面的旋转动画设置了无限循环(RepeatCount = INFINITE)
        // 所以动画不会结束,而是无限循环
        // 所以组合动画的下面两行设置是无效的
        setAnimation.setRepeatMode(Animation.RESTART);
        setAnimation.setRepeatCount(1);// 设置了循环一次,但无效

        // 步骤3:逐个创建子动画(方式同单个动画创建方式,此处不作过多描述)

        // 子动画1:旋转动画
        Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
        rotate.setDuration(1000);
        rotate.setRepeatMode(Animation.RESTART);
        rotate.setRepeatCount(Animation.INFINITE);

        // 子动画2:平移动画
        Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,
                TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
                TranslateAnimation.RELATIVE_TO_SELF,0
                ,TranslateAnimation.RELATIVE_TO_SELF,0);
        translate.setDuration(10000);

        // 子动画3:透明度动画
        Animation alpha = new AlphaAnimation(1,0);
        alpha.setDuration(3000);
        alpha.setStartOffset(7000);

        // 子动画4:缩放动画
        Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
        scale1.setDuration(1000);
        scale1.setStartOffset(4000);

        // 步骤4:将创建的子动画添加到组合动画里
        setAnimation.addAnimation(alpha);
        setAnimation.addAnimation(rotate);
        setAnimation.addAnimation(translate);
        setAnimation.addAnimation(scale1);

        mButton.startAnimation(setAnimation);
        // 步骤5:播放动画

3.1.5应用场景

  • Activity 的切换效果
  • Fragment动画切换效果
  • 视图组(ViewGroup)中子元素的出场效果

a.启动动画

Intent intent = new Intent (this,Acvtivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
// 采用overridePendingTransition(int enterAnim, int exitAnim)进行设置
// enterAnim:从Activity a跳转到Activity b,进入b时的动画效果资源ID
// exitAnim:从Activity a跳转到Activity b,离开a时的动画效果资源Id

// 特别注意
// overridePendingTransition()必须要在startActivity(intent)后被调用才能生效

b.退出动画

@Override
public void finish(){
    super.finish();

    overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
// 采用overridePendingTransition(int enterAnim, int exitAnim)进行设置
// enterAnim:从Activity a跳转到Activity b,进入b时的动画效果资源ID
// exitAnim:从Activity a跳转到Activity b,离开a时的动画效果资源Id

// 特别注意
// overridePendingTransition()必须要在finish()后被调用才能生效
}

3.2逐帧动画

Android动画_第3张图片
image.png

3.2.1作用对象

1.如Android的TextView、Button等等
2.不可作用于View组件的属性,如:颜色、背景、长度等等

3.2.2原理

  • 将动画拆分为 帧 的形式,且定义每一帧 = 每一张图片
  • 逐帧动画的本质:按序播放一组预先定义好的图片

3.2.3具体使用

步骤1:将动画资源(即每张图片资源)放到 drawable文件夹里

技巧:

  1. 找到自己需要的gif动画
  2. 用 gif分解软件(如 GifSplitter)将 gif 分解成一张张图片即可

步骤2:设置 & 启动 动画

  • 方式一:xml实现
    步骤1:在 res/anim的文件夹里创建动画效果.xml文件


    
    


步骤3:在Java代码中载入 & 启动动画

  private void start() {
        iv.setImageResource(R.drawable.anim_frame);
        animationDrawable = (AnimationDrawable) iv.getDrawable();
        animationDrawable.start();
    }

    private void end() {
        iv.setImageResource(R.drawable.anim_frame);
        animationDrawable = (AnimationDrawable) iv.getDrawable();
        animationDrawable.stop();
    }
  • 方式二:java实现
  animationDrawable = new AnimationDrawable();
        for (int i = 1; i < 3; i++) {
            int id = getResources().getIdentifier("ic_jyy" + i, "drawable", getPackageName());
            Drawable drawable = getResources().getDrawable(id);
            animationDrawable.addFrame(drawable, 100);
        }
  private void start2() {
        animationDrawable.setOneShot(false);
        iv.setImageDrawable(animationDrawable);
        // 特别注意:在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次
        animationDrawable.stop();
        animationDrawable.start();
    }

    private void end2() {
        animationDrawable.setOneShot(true);
        iv.setImageDrawable(animationDrawable);
        animationDrawable.stop();
    }

3.2.4特点

  • 优点:使用简单、方便
  • 缺点:容易引起 OOM,因为会使用大量 & 尺寸较大的图片资源

尽量避免使用尺寸较大的图片

3.2.5应用场景

较为复杂的个性化动画效果。


4.属性动画

Android动画_第4张图片
image.png

4.1 为什么要使用属性动画

  • 属性动画(Property Animation)是在 Android 3.0(API 11)后才提供的一种全新动画模式
  • 那么为什么要提供属性动画(Property Animation)?
  • 具体请看下图


    Android动画_第5张图片
    image.png

4.2简介

  • 作用对象:任意 Java 对象

不再局限于 视图View对象

  • 实现的动画效果:可自定义各种动画效果

不再局限于4种基本变换:平移、旋转、缩放 & 透明度

4.3特点

  • 作用对象进行了扩展:不只是View对象,甚至没对象也可以
  • 动画效果:不只是4种基本变换,还有其他动画效果
  • 作用领域:API11后引入的

4.4工作原理

  • 在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果

可以是任意对象的任意属性

  • 具体的工作原理逻辑如下:


    Android动画_第6张图片
    image.png
  • 从上述工作原理可以看出属性动画有两个非常重要的类:ValueAnimator 类 & ObjectAnimator 类
  • 其实属性动画的使用基本都是依靠这两个类

4.5使用

4.5.1ValueAnimator(其实最少用到)

  • 定义:属性动画机制中 最核心的一个类
  • 实现动画的原理:通过不断控制 值 的变化,再不断 手动 赋给对象的属性,从而实现动画效果。如图下:
    Android动画_第7张图片
    image.png

    三个重要方法:
    1.ValueAnimator.ofInt(int values)
    2.ValueAnimator.ofFloat(float values)
    3.ValueAnimator.ofObject(int values)

4.5.1.1ValueAnimator.ofInt(int values)

日常更多的是使用ValueAnimator.ofFloat(float values),且使用方法类似,这里略

4.5.1.2ValueAnimator.oFloat(float values)

  • 作用:将初始值 以浮点型数值的形式 过渡到结束值
  • 工作原理:


    Android动画_第8张图片
    image.png
  • 具体使用:分为 XML 设置 / Java 代码设置

设置方法1:在 Java 代码中设置

ValueAnimator anim = ValueAnimator.ofFloat(0, 3);  
// 其他使用类似ValueAnimator.ofInt(int values),此处不作过多描述

设置方法2:在XML 代码中设置

  • 步骤1:在路径 res/animator的文件夹里创建相应的动画.xml文件

此处设置为res/animator/set_animation.xml

  • 步骤2:设置动画参数
    set_animation.xml
// ValueAnimator采用  标签
  
  • 步骤3:在Java代码中启动动画
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.set_animation);  
// 载入XML动画

animator.setTarget(view);  
// 设置动画对象

animator.start();  
// 启动动画

效果图

Android动画_第9张图片
image.png

ValueAnimator.ofInt()与ValueAnimator.oFloat()仅仅只是在估值器上的区别


4.5.1.3ValueAnimator.ofObject()

  • 作用:将初始值 以对象的形式 过渡到结束值

即通过操作 对象 实现动画效果

  • 工作原理:


    Android动画_第10张图片
    image.png
  • 具体使用:
public class Point {

    private float x;

    private float y;

    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }
}
public class PointEvaluator implements TypeEvaluator {

    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        // 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;

        // 根据fraction来计算当前动画的x和y的值
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());

        // 将计算后的坐标封装到一个新的Point对象中并返回
        return new Point(x, y);
    }
}
// 步骤1:创建初始动画时的对象点  & 结束动画时的对象点
Point startPoint = new Point(RADIUS, RADIUS);
Point endPoint = new Point(Util.dp2px(300), Util.dp2px(500));

// 步骤2:创建动画对象 & 设置初始值 和 结束值
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);

// 步骤3:设置动画参数
anim.setDuration(5000);
// 此处是将 改变后的坐标值对象 赋给 当前的坐标值对象
// 设置 值的更新监听器
// 即每当坐标值(Point对象)更新一次,该方法就会被调用一次
anim.addUpdateListener(valueAnimator -> {
    // 将每次变化后的坐标值(估值器PointEvaluator中evaluate()返回的Piont对象值)到当前坐标值对象(currentPoint)
    // 从而更新当前坐标值(currentPoint)

    // 步骤4:每次赋值后就重新绘制,从而实现动画效果
    currentPoint = (Point) valueAnimator.getAnimatedValue();
    invalidate();
});
anim.start();

4.5.2ViewPropertyAnimator

  • 使用:View.animate()创建对象,以及使用ViewPropertyAnimator.translationX()等方法来设置动画
  • 可以设置多个动画
  • 可以用setDurantion()设置持续时间
  • 可以用setStartDealy()来设置开始延时

4.5.3 ObjectAnimator

4.5.3.1原理

通过不断控制 值 的变化,再不断 自动 赋给对象的属性,从而实现动画效果。如下图:


Android动画_第11张图片
image.png

从上面的工作原理可以看出:ObjectAnimator与 ValueAnimator类的区别:

  • ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;
  • ObjectAnimator 类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作;

4.5.3.2使用

  • 设置方式1:Java 设置
ObjectAnimator animator = ObjectAnimator.ofFloat(Object object, String property, float ....values);  

// ofFloat()作用有两个
// 1. 创建动画实例
// 2. 参数设置:参数说明如下
// Object object:需要操作的对象
// String property:需要操作的对象的属性
// float ....values:动画初始值 & 结束值(不固定长度)
// 若是两个参数a,b,则动画效果则是从属性的a值到b值
// 若是三个参数a,b,c,则则动画效果则是从属性的a值到b值再到c值
// 以此类推
// 至于如何从初始值 过渡到 结束值,同样是由估值器决定,此处ObjectAnimator.ofFloat()是有系统内置的浮点型估值器FloatEvaluator,同ValueAnimator讲解

anim.setDuration(500);
        // 设置动画运行的时长

        anim.setStartDelay(500);
        // 设置动画延迟播放时间

        anim.setRepeatCount(0);
        // 设置动画重复播放次数 = 重放次数+1
        // 动画播放次数 = infinite时,动画无限重复

        anim.setRepeatMode(ValueAnimator.RESTART);
        // 设置重复播放动画模式
        // ValueAnimator.RESTART(默认):正序重放
        // ValueAnimator.REVERSE:倒序回放

animator.start();  
// 启动动画
  • 设置方法2:在XML 代码中设置
    步骤1:在路径 res/animator 的文件夹里创建动画效果.xml文件

此处设置为res/animator/set_animation.xml

步骤2:设置动画参数
set_animation.xml

// ObjectAnimator 采用  标签
 

在java代码中启动动画

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.view_animation);  
// 载入XML动画

animator.setTarget(view);  
// 设置动画对象

animator.start();  
// 启动动画
属性 作用 数值类型
Alpha 控制View的透明度 float
TranslationX 控制X方向的位移 float
TranslationY 控制Y方向的位移 float
ScaleX 控制X方向的缩放倍数 float
ScaleY 控制Y方向的缩放倍数 float
Rotation 控制以屏幕方向为轴的旋转度数 float
RotationX 控制以X轴为轴的旋转度数 float
RotationY 控制以Y轴为轴的旋转度数 float

4.5.3.3自定义动画

比如,自定义radius属性

ObjectAnimator animator = ObjectAnimator.ofObject(view, "radius", Utils.dp2px(200));

自定义属性需要设置getter和setter方法,并且setter方法里需调用invalidate()来出发重绘:

public float getRadius() {    return radius; }
public void setRadius(float radius) {    this.radius = radius;    invalidate(); }

5.插值器(Interpolator)

  • 插值器,用于设置时间完成度到动画完成度的计算公式,直白的说即设置动画的速度曲线,通过setInterpolator(Interpolator)方法设置
  • 常用的有AccelerateDecelerateInterpolator AccelerateInterpolator DecelerateInterpolator LinearInterpolator

5.2 PropertyValuesHolder

用于设置更加详细的动画,例如多个属性应用同一个对象

        PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("leftFlip", 0);
        PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("rightFlip", 0.5f);
        PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("flipRotation", 540f);
        PropertyValuesHolder holder4 = PropertyValuesHolder.ofFloat("leftFlip", 0.5f);
        PropertyValuesHolder holder5 = PropertyValuesHolder.ofFloat("rightFlip", -0.5f);

        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(rayView, holder1, holder2, holder3, holder4, holder5);
        animator.setDuration(3000);
        animator.start();

或者配合使用Keyframe,对一个属性分多个阶段

        float distanceY = Util.dp2px(400);
        Keyframe key1 = Keyframe.ofFloat(0, 0);
        Keyframe key2 = Keyframe.ofFloat(0.4f, distanceY * 0.2f );
        Keyframe key3 = Keyframe.ofFloat(0.6f, distanceY * 0.9f);
        Keyframe key4 = Keyframe.ofFloat(0.9f, distanceY * 1.2f);
        Keyframe key5 = Keyframe.ofFloat(1f, distanceY * 1f);

        PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("translationY", key1, key2, key3, key4, key5);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(rayView, holder);
        animator.setDuration(3000);
        animator.start();

5.3 AnimatorSet

将多个Animator合并在一起使用,先后顺序或并列顺序都可以

        ObjectAnimator leftAnim = ObjectAnimator.ofFloat(rayView, "leftFlip", 0);
        leftAnim.setDuration(1000);

        ObjectAnimator rightAnim = ObjectAnimator.ofFloat(rayView, "rightFlip", 0.5f);
        rightAnim.setDuration(1000);

        ObjectAnimator rotateAnim = ObjectAnimator.ofFloat(rayView, "flipRotation", 540f);
        rotateAnim.setDuration(1500);

        ObjectAnimator leftAnim2 = ObjectAnimator.ofFloat(rayView, "leftFlip", 0.5f);
        leftAnim2.setDuration(1000);

        ObjectAnimator rightAnim2 = ObjectAnimator.ofFloat(rayView, "rightFlip", -0.5f);
        rightAnim2.setDuration(1000);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playSequentially(leftAnim, rightAnim, rotateAnim, leftAnim2, rightAnim2);
        animatorSet.start();

6.估值器(TypeEvaluator)

  • 作用:设置动画完成程度到属性具体值的计算公式。默认的ofInt() ofFloat()已经有了自带的IntEvaluator FloatEvaluator。有时我们需要自定义Evaluator。如颜色,需要为int类类型的颜色设置ArgbEvaluator,而不是让他们使用IntEvaluator

animator.setEvaluator(new ArgbEvaluator())

估值器插值器比较:

插值器(Interpolator)决定 值 的变化模式(匀速、加速blabla)
估值器(TypeEvaluator)决定 值 的具体变化数值


Android动画_第12张图片
image.png

7.转场动画(Transition FrameWork)

  • 不同Activity之间切换时,Activityc的内容(contentView)转场动画
  • 不同Activity之间切换时,如果使用了Shared Element动画,也可以使用Transition FrameWork来实现不同的过渡动画效果
  • 同一个Activity内View变化的过渡动画(Scene)

7.1Activity之间切换的过渡动画

已经有三种现成的动画可以用:Explode,Slide和Fade

  • xml中创建

res/transition/activity_fade.xml



@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_anim_framework2);
        initWindowAnim();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void initWindowAnim() {
        Fade fade = (Fade) TransitionInflater.from(this).inflateTransition(R.transition.activity_fade);
        getWindow().setEnterTransition(fade);
    }
  • java中创建
 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_anim_framework2);
        initWindowAnim();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void initWindowAnim() {
        getWindow().setEnterTransition(new Slide().setDuration(2000));
        getWindow().setExitTransition(new Slide().setDuration(2000));
    }

那么这里面一步一步的到底发生了什么:

  1. Activity A 启动 Activity B
  2. Transition Framework 发现 A 中定义了Exit Transition (slide) 然后就会对它的所有可见的View使用这个过渡动画.
  3. Transition Framework 发现 B 中定义了Enter Transition (fade) 然后机会对它所有可见的Views使用这个过渡动画.
  4. On Back Pressed(按返回键) Transition Framework 会执行把 Enter and Exit过渡动画反过来执行(但是如果定义了 returnTransition和reenterTransition,那么就会执行这些定义的动画)

  • Exit Transition:可以理解为activity进入后台的过渡动画
  • Enter Transition:可以理解为创建activity并显示时的过渡动画
  • Return Transition:可以理解为销毁activity时的过渡动画
  • Reenter Transition:可以理解为activity从后台进入前台时的过渡动画
  • 要使这些过渡动画生效,我们需要调用startActivity(intent,bundle)方法来启动activity。bundle需要通过
    注意:Return and Reenter Transitions是与进入和退出动画相对应的.
    EnterTransition <--> ReturnTransition
    ExitTransition <--> ReenterTransition
    如果Return or Reenter没有创建, Android 会把Enter and Exit Transitions反过来执行. 但是如果你创建了Return or Reenter,那Android就会执行你创建的动画,并且这些动画可以不同.

启动方式

        startActivity(new Intent(this, ActivityAnimFrameworkExplode.class)
                , ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle());

7.2Activity之间共享元素(Share Elements)

a.设置Window Content Transition属性


但是我没有设置也是没什么问题的

b.设置相同的transition name

为了使共享元素动画生效,你需要给共享元素的两个View设置相同的android:transitionName属性值。不过他们的id和其他属性可以不同。

第一个页面xml

        

第二个页面xml

  

c.用共享元素来启动activity

startActivity(new Intent(this, ActivityAnimFrameworkShareElements.class)
                , ActivityOptionsCompat.makeSceneTransitionAnimation(this, tvDealy, "dealy").toBundle());

多个元素也可

        Intent intent = new Intent(this, ActivityAnimFrameworkShareElements2.class);

        Pair jcPair = new Pair<>(tvJc, ViewCompat.getTransitionName(tvJc));
        Pair dealyPair = new Pair<>(tvDealy, ViewCompat.getTransitionName(tvDealy));
        Pair goPair = new Pair<>(tvGo, ViewCompat.getTransitionName(tvGo));
        Pair mvPair = new Pair<>(tvMv, ViewCompat.getTransitionName(tvMv));
        ActivityOptionsCompat pair = ActivityOptionsCompat.makeSceneTransitionAnimation(this, jcPair, dealyPair, goPair, mvPair);
        ActivityCompat.startActivity(this, intent, pair.toBundle());

7.3 fragment里使用

fragment其实同理

FragmentB fragmentB = FragmentB.newInstance(sample);

// Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition);

// Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition);

getFragmentManager().beginTransaction()
        .replace(R.id.content, fragmentB)
        .addSharedElement(blueView, getString(R.string.blue_name))
        .commit();

允许过渡效果之间的重叠
fragment.setAllowEnterTransitionOverlap(boolean)
fragment.setAllowReturnTransitionOverlap(boolean)

当设置为true,enter transition会立马执行>
当设置为false,enter transition会等到退出exit transition结束后再执行.

FragmentB fragmentB = FragmentB.newInstance(sample);

// Defines enter transition for all fragment views
Slide slideTransition = new Slide(Gravity.RIGHT);
slideTransition.setDuration(1000);
sharedElementFragment2.setEnterTransition(slideTransition);

// Defines enter transition only for shared element
ChangeBounds changeBoundsTransition = TransitionInflater.from(this).inflateTransition(R.transition.change_bounds);
fragmentB.setSharedElementEnterTransition(changeBoundsTransition);

// Prevent transitions for overlapping
fragmentB.setAllowEnterTransitionOverlap(overlap);
fragmentB.setAllowReturnTransitionOverlap(overlap);

getFragmentManager().beginTransaction()
        .replace(R.id.content, fragmentB)
        .addSharedElement(blueView, getString(R.string.blue_name))
        .commit()

7.4布局元素动画(Scenes)

s1 = new Scene(mRootView, mRootView.findViewById(R.id.root_s1));
s2 = Scene.getSceneForLayout(mRootView, R.layout.layout_share2, this);
s3 = Scene.getSceneForLayout(mRootView, R.layout.layout_share3, this);
s4 = Scene.getSceneForLayout(mRootView, R.layout.layout_share4, this);

  @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @OnClick(R.id.btn_s1)
    public void s1() {
        TransitionManager.go(s1);
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @OnClick(R.id.btn_s2)
    public void s2() {
        TransitionManager.go(s2, new ChangeBounds());
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @OnClick(R.id.btn_s3)
    public void s3() {
        TransitionManager.go(s3, new Explode());
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @OnClick(R.id.btn_s4)
    public void s4() {
        Slide slide = new Slide();
        slide.setSlideEdge(Gravity.RIGHT);
        slide.setDuration(2000);

        TransitionManager.go(s4, slide);
    }

7.5ViewAnimationUtils

目前ViewAnimationUtils类中只有一个方法,那就是createCircularReveal。很明显,我们使用ViewAnimationUtils.createCircularReveal()方法就能达到基本的揭露动画效果了

  • view:代表的是你要操作的view
  • centerX:圆的x方向的中点
  • centerY:圆的y方向的中点
  • startRadius:这个圆开始时候的半径
  • endRadius:结束时候的半径

7.6共享元素(share elements)+圆形展现(Circular Reveal)

圆形展现仅仅一个现实和隐藏一组view的动画而已。API21+可以通过ViewAnimationUtils来使用它。

Circular Reveal动画可以结合共享元素过渡效果来创建一些有意义的动画来告诉用户app在发生中什么。

ViewAnimationUtils.createCircularReveal()实现

点击触摸点产生一个Circular Reveal动画


    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @OnClick(R.id.tv_mv)
    public void go7(View v) {
        vReveal.setVisibility(View.VISIBLE);
        vReveal2.setVisibility(View.GONE);
        int centerX = (v.getLeft() + v.getRight()) / 2;
        int centerY = (v.getTop() + v.getBottom()) / 2;
        float finalRadius = (float) Math.hypot((double) centerX, (double) centerY);
        Animator mCircularReveal = ViewAnimationUtils.createCircularReveal(
                vReveal, centerX, centerY, 0, finalRadius);

        mCircularReveal.setDuration(4000).start();
    }

也可以和其他动画一起做成跟炫的动画


    public void launchTwitter(View view) {

        /*
         MARGIN_RIGHT = 16dp
         FAB_BUTTON_RADIUS = 28dp
         */
        width = imageView.getWidth();
        height = imageView.getHeight();

        x = width / 2;
        y = height / 2;
        hypotenuse = (int) Math.hypot(x, y);

        x = (int) (x - ((16 * pixelDensity) + (28 * pixelDensity)));

        FrameLayout.LayoutParams parameters = (FrameLayout.LayoutParams)
                revealView.getLayoutParams();
        parameters.height = imageView.getHeight();
        revealView.setLayoutParams(parameters);

        imageButton.animate()
                .translationX(-x)
                .translationY(-y)
                .setDuration(200)
                .setListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animator) {

                    }

                    @Override
                    public void onAnimationEnd(Animator animator) {

                        Animator anim = ViewAnimationUtils.createCircularReveal(revealView, width / 2, height / 2, 28 * pixelDensity, hypotenuse);
                        anim.setDuration(350);
                        anim.addListener(new Animator.AnimatorListener() {
                            @Override
                            public void onAnimationStart(Animator animator) {
                                System.out.println("start");
                            }

                            @Override
                            public void onAnimationEnd(Animator animator) {
                                layoutButtons.setVisibility(View.VISIBLE);
                                closeButton.setVisibility(View.VISIBLE);
                                layoutButtons.startAnimation(alphaAppear);
                                closeButton.startAnimation(alphaAppear);
                                System.out.println("startend");

                            }

                            @Override
                            public void onAnimationCancel(Animator animator) {

                            }

                            @Override
                            public void onAnimationRepeat(Animator animator) {

                            }
                        });
                        imageButton.setVisibility(View.GONE);
                        revealView.setVisibility(View.VISIBLE);
                        anim.start();
                    }

                    @Override
                    public void onAnimationCancel(Animator animator) {

                    }

                    @Override
                    public void onAnimationRepeat(Animator animator) {

                    }
                });
    }

    public void closeTwitter(View view) {

        alphaDisappear.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                Animator anim = ViewAnimationUtils.createCircularReveal(revealView, width / 2, height / 2, hypotenuse, 28 * pixelDensity);
                anim.setDuration(350);
                anim.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animator) {

                    }

                    @Override
                    public void onAnimationEnd(Animator animator) {
                        revealView.setVisibility(View.GONE);
                        imageButton.setVisibility(View.VISIBLE);
                        imageButton.animate()
                                .translationX(0f)
                                .translationY(0f)
                                .setDuration(200)
                                .setListener(null);
                    }

                    @Override
                    public void onAnimationCancel(Animator animator) {

                    }

                    @Override
                    public void onAnimationRepeat(Animator animator) {

                    }
                });
                anim.start();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        layoutButtons.setVisibility(View.GONE);
        closeButton.setVisibility(View.GONE);
        layoutButtons.startAnimation(alphaDisappear);
        closeButton.startAnimation(alphaDisappear);
    }

之前分享
更多案例


总结

希望大家可以在实际工作中用到上面的知识。之后也会做一个动画库。谢谢大家。

你可能感兴趣的:(Android动画)