Android动画

2019-03-11  本文已影响0人  我要这八块腹肌有何用

1.为什么要说动画?

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

3.视图动画(View Animation)

3.1补间动画(Tween Animation)

image.png

3.1.1作用对象

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

3.1.2原理

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

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

3.1.3分类

image.png

3.1.4具体使用

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

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

3.1.4.1平移动画

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

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

<?xml version="1.0" encoding="utf-8"?>
// 采用<translate /> 标签表示平移动画
<translate xmlns:android="http://schemas.android.com/apk/res/android"

    // 以下参数是4种动画效果的公共属性,即都有的属性
    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:fromXDelta="0" // 视图在水平方向x 移动的起始值
    android:toXDelta="500" // 视图在水平方向x 移动的结束值

    android:fromYDelta="0" // 视图在竖直方向y 移动的起始值
    android:toYDelta="500" // 视图在竖直方向y 移动的结束值

    /> 
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组合动画

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

3.1.4.5.1在XML 代码中设置

此处为res/anim/view_animation.xml

<?xml version="1.0" encoding="utf-8"?>
// 采用< Set/>标签
<set xmlns:android="http://schemas.android.com/apk/res/android">

// 组合动画同样具备公共属性
    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属性来延迟单个动画播放时间

// 设置旋转动画,语法同单个动画
    <rotate
        android:duration="1000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatMode="restart"
        android:repeatCount="infinite"
        />

// 设置平移动画,语法同单个动画
    <translate
        android:duration="10000"
        android:startOffset = “1000”// 延迟该动画播放时间
        android:fromXDelta="-50%p"
        android:fromYDelta="0"
        android:toXDelta="50%p"
        android:toYDelta="0" />

// 设置透明度动画,语法同单个动画
    <alpha
        android:startOffset="7000"
        android:duration="3000"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />


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

</set>
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应用场景

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逐帧动画

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:设置 & 启动 动画

<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <item android:drawable="@drawable/ic_jyy1" android:duration="300"/>
    <item android:drawable="@drawable/ic_jyy2" android:duration="300"/>

</animation-list>

步骤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();
    }
  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特点

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

3.2.5应用场景

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


4.属性动画

image.png

4.1 为什么要使用属性动画

4.2简介

不再局限于 视图View对象

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

4.3特点

4.4工作原理

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

4.5使用

4.5.1ValueAnimator(其实最少用到)

4.5.1.1ValueAnimator.ofInt(int values)

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

4.5.1.2ValueAnimator.oFloat(float values)

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

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

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

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

// ValueAnimator采用<animator>  标签
<animator xmlns:android="http://schemas.android.com/apk/res/android"  
    // 设置属性同上
    android:valueFrom="0"  
    android:valueTo="100"  
    android:valueType="floatType"/>  
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.set_animation);  
// 载入XML动画

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

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

效果图

image.png

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


4.5.1.3ValueAnimator.ofObject()

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

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


4.5.3 ObjectAnimator

4.5.3.1原理

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


image.png

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

4.5.3.2使用

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();  
// 启动动画

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

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

// ObjectAnimator 采用<animator>  标签
<ObjectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"   // 初始值
    android:valueTo="0"  // 结束值
    android:valueType="floatType"  // 变化值类型 :floatType & intType
    android:propertyName="alpha" // 对象变化的属性名称
/> 

在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)

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)

animator.setEvaluator(new ArgbEvaluator())

估值器插值器比较:

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


image.png

7.转场动画(Transition FrameWork)

7.1Activity之间切换的过渡动画

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

res/transition/activity_fade.xml

<?xml version="1.0" encoding="utf-8"?>
<fade xmlns:android="http://schemas.android.com/apk/res/"
    android:duration="1000"/>
@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);
    }
 @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,那么就会执行这些定义的动画)

启动方式

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

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

a.设置Window Content Transition属性

<style name="MaterialAnimations" parent="@style/Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="android:windowContentTransitions">true</item
    ...
</style>

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

b.设置相同的transition name

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

第一个页面xml

        <TextView
            android:id="@+id/tv_dealy"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="30dp"
            android:drawableStart="@mipmap/dealy_logo"
            android:drawablePadding="@dimen/dim_dp15"
            android:gravity="center"
            android:text="Dealy"
            android:textColor="@color/color_333"
            android:textSize="18sp"
            android:transitionName="dealy" />

第二个页面xml

  <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:src="@mipmap/bg"
        android:transitionName="dealy" />

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()方法就能达到基本的揭露动画效果了


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);
    }

之前分享
更多案例


总结

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

上一篇 下一篇

猜你喜欢

热点阅读