1111111111

1      概述

在3.0系统之前,Android给我们提供了逐帧动画Frame Animation和补间动画Tween Animation两种动画:

1)        逐帧动画的原理很简单,就是将一个完整的动画拆分成一张张单独的图片,然后将它们连贯起来进行播放;

2)        补间动画是专门为View提供的动画,可以实现View的透明度、缩放、平移和旋转四种效果。

补间动画有两个个缺陷:

1)        补间动画只能对View设置动画,对非View的对象不能设置动画;

2)        补间动画只是改变了View的显示效果而没有真正的改变View的属性。例如,我们想使用补间动画将一个按钮从一个位置移动到一个新的位置,那么当移动完成之后我们点击这个按钮,是不会触发其点击事件的,而当我们点击移动前的位置时,会触发其点击事件,即补间动画只是在另一个地方重新绘制了这个View,其他的东西都没有改变。

在3.0系统之后,Android为我们提供了一种新的动画——Animator属性动画。在属性动画中,我们不仅可以像补间动画那样设置控件的透明度、缩放、平移或旋转的动画,还可以做到将这些动画联合起来播放、将一组动画按顺序播放、控制动画的播放速度,甚至可以对非View设置动画等等。

属性动画,顾名思义,是对对象的属性设置的动画。简单的说,只要一个对象的某个属性有set和get方法,就可以对其设置属性动画。一句话概括,属性动画就是不断的改变一个对象的某个属性。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束只,剩下的工作就可以全部交给系统去完成了。

正是因为属性动画是对属性的动画,因此补间动画的第二个缺陷就不复存在了。使用属性动画移动一个按钮,那么这个按钮就是真的被移动了,而不仅仅是在另一个地方重新绘制了自己那么简单。

2      ObjectAnimator

ObjectAnimator是属性动画中最常用的一个类,我们可以通过它直接控制一个对象的属性。使用ObjectAnimator时,我们只需要我们想修改哪个对象的哪个属性、属性的起始值和结束值,以及动画的持续时间,系统就可以为我们运行动画了。ObjectAnimator的使用方法如下。

执行单个动画:

ObjectAnimator.ofFloat(image, "translationX", 0f, 500f) // 初始化动画,设置各个参数
        .setDuration(3000) // 设置动画持续时间
        .start(); // 开始运行动画

上面代码中,ofFloat()方法的第一个参数是动画作用的对象,这里是一个ImageView;第二个参数是属性名称,这里指定的是X轴的平移;第三个参数是一个不定长参数,指定属性的起始值和结束值;setDuration()方法指定的是动画执行的时长,这里是3秒钟;最后调用start()方法,动画就开始执行了。

ObjectAnimator不仅有ofFloat()方法,还有很多其他方法,例如ofInt()、ofObject()、ofPropertyValuesHolder()等,这些方法会在后面详细介绍。

如果想要同时执行多个动画,只需要指定多个动画后分别调用start()方法即可,代码如下:

ObjectAnimator.ofFloat(image, "translationX", 0f, 500f).setDuration(3000).start();
ObjectAnimator.ofFloat(image, "translationY", 0f, 500f).setDuration(3000).start();
ObjectAnimator.ofFloat(image, "rotation", 0f, 360f).setDuration(3000).start();
为了加深对ObejctAnimator的理解,这里我们使用ObjectAnimator实现一个卫星菜单的效果,效果图如下:

                    

如上图所示,开始的时候七个菜单图标是隐藏在右下角的红色按钮底下的,如左图所示;当我们点击了红色按钮的时候,其他菜单图标就会弹出,像卫星一样围绕早红色按钮外层,如右图所示。实现这个效果的核心代码如下:

for (int i = 0; i < menus.size(); i++) {
    ImageView menu = menus.get(i);
    double angle = Math.toRadians(i * (90 * 1.0 / (menus.size() - 1))); // 角度
    double radius = 450; // 半径
    float distanceX = (float) (Math.cos(angle) * radius); // X坐标偏移量
    float distanceY = (float) (Math.sin(angle) * radius); // Y坐标偏移量
    ObjectAnimator animatorX;
    ObjectAnimator animatorY;
    if (isOpen) { // 如果菜单是打开的则关闭菜单
        animatorX = ObjectAnimator.ofFloat(menu, "translationX", -distanceX, 0f);
        animatorY = ObjectAnimator.ofFloat(menu, "translationY", -distanceY, 0f);
    } else { // 如果菜单是关闭的则打开菜单
        animatorX = ObjectAnimator.ofFloat(menu, "translationX", 0f, -distanceX);
        animatorY = ObjectAnimator.ofFloat(menu, "translationY", 0f, -distanceY);
    }
    AnimatorSet set = new AnimatorSet(); // X、Y轴同时移动
    set.playTogether(animatorX, animatorY);
    set.setDuration(500);
    set.setInterpolator(new BounceInterpolator());
    set.start();
}

3      组合动画

所谓的组合动画,就是将多个动画同时播放,或将一组动画按顺序播放。在上面介绍ObjectAnimator时我们已经介绍了一种方式,就是指定多个动画后分别调用start()方法。下面介绍其他几种方法:

方法二:使用PropertyValuesHolder:

PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("translationX", 0f, 500f);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("translationY", 0f, 500f);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
ObjectAnimator.ofPropertyValuesHolder(image, holder1, holder2, holder3).setDuration(3000).start();
方法三:使用AnimatorSet:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(image, "translationX", 0f, 500f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(image, "translationY", 0f, 500f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(image, "rotation", 0f, 360f);
AnimatorSet set = new AnimatorSet();
set.setDuration(3000);
set.playTogether(animator1, animator2, animator3);
set.start();
上面说到,属性动画不光可以让一组动画同时播放,还可以按一定顺序播放动画。按顺序播放动画的几种方式代码如下:

方法一:使用AnimatorSet对象中的API实现:

ObjectAnimator animator1 = ObjectAnimator.ofFloat(image, "translationX", 0f, 500f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(image, "translationY", 0f, 500f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(image, "rotationX", 0f, 360f);
ObjectAnimator animator4 = ObjectAnimator.ofFloat(image, "rotationY", 0f, 360f);
AnimatorSet set = new AnimatorSet();
set.play(animator3).before(animator2).after(animator1).with(animator4);
set.setDuration(3000);
set.start();
方法二:设置动画监听事件AnimatorListener:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(image, "alpha", 0f, 1f);
final ObjectAnimator animator2 = ObjectAnimator.ofFloat(image, "translationX", 0f, 500f);
final ObjectAnimator animator3 = ObjectAnimator.ofFloat(image, "translationY", 0f, 500f);
animator1.setDuration(3000);
animator2.setDuration(3000);
animator3.setDuration(3000);
// 设置属性动画的监听事件(使用AnimatorListener必须要监听所有四个事件)
animator1.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        animator2.start();
    }
    @Override
    public void onAnimationEnd(Animator animation) {
        animator3.start();
    }
    @Override
    public void onAnimationCancel(Animator animation) {
    }
    @Override
    public void onAnimationRepeat(Animator animation) {
    }
});
animator1.start();
方法三:设置动画监听事件AnimatorListenerAdapter:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(image, "alpha", 0f, 1f);
final ObjectAnimator animator2 = ObjectAnimator.ofFloat(image, "translationX", 0f, 500f);
final ObjectAnimator animator3 = ObjectAnimator.ofFloat(image, "translationY", 0f, 500f);
animator1.setDuration(3000);
animator2.setDuration(3000);
animator3.setDuration(3000);
// 设置属性动画的监听事件(使用AnimatorListenerAdapter可以选择不监听所有事件)
animator1.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        animator3.start(); // 在animator1执行完后执行animator3
    }
    @Override
    public void onAnimationStart(Animator animation) {
        animator2.start(); //在animator1执行的同时执行animator2
    }
});
animator1.start();

4      ValueAnimator

ValueAnimator是整个属性动画最核心的类,ObjectAnimator类就是ValueAnimator类的一个子类。前面我们说过,属性动画的原理就是通过不断改变对象的属性值来实现过渡的,而这种过渡就是通过ValueAnimator类来负责计算的。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式和动画监听等。

ValueAnimator的使用方法和ObjectAnimator的使用方法基本类似,不同的是ValueAnimator中不能指定运行动画的对象,因此ValueAnimator往往需要设置一个动画监听,通过不断监听当前动画运行到的属性值来动态的进行处理。代码如下:

ValueAnimator animator = ValueAnimator.ofInt(0, 100); // 产生一个从0到100变化的整数的动画
animator.setDuration(2000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Integer value = (Integer) animation.getAnimatedValue(); // 动态的获取当前运行到的属性值
        ((Button) view).setText(value + "");
    }
});
animator.start(); // 开始播放动画
和ObjectAnimator一样,除了ofFloat()之外,ValueAnimator也有一些其他的方法,如ofPropertyValuesHolder()、ofObject()、ofInt()等方法。

除了上面的功能,ValueAnimator还可以通过setRepeatCount()和setRepeatMode()方法来设置动画重复的次数和播放模式、通过setStartDelay()方法来设置动画延迟播放的时间。这些方法也都可以在ObjectAnimator中使用。

5      Animator监听器

在“组合动画”章节中我们已经接触过Animator的监听器了。常用的Animator监听器有AnimatorListener和AnimatorListenerAdapter,都是通过Animator对象的addListener()方法设置的。

如果添加的监听器是AnimatorListener,那么就必须要实现onAnimationStart()、onAnimationRepeat()、onAnimationEnd()和onAnimationCancel()四个方法,示例代码如下:

animator.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animation) {
        // 动画开始的监听事件
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        // 动画结束的监听事件
    }

    @Override
    public void onAnimationCancel(Animator animation) {
        // 动画取消的监听事件
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
        // 动画重播的监听事件
    }
});
如果添加的是AnimatorListenerAdapter,那么就不必一一实现这四个方法,而是可以根据自己的需要自定义实现,示例代码如下:
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        // 动画结束的监听事件
    }

    @Override
    public void onAnimationStart(Animator animation) {
        // 动画开始的监听事件
    }

    // 其他两个事件可以选择不实现
});

6      使用XML编写动画

过去的补间动画可以通过XML的方式编写,属性动画也可以编写到XML文件中。编写到XML文件中的一个好处是可以方便的实现动画的重用。

我们需要在项目的res目录下创建一个名为animator的文件夹,在这个文件夹中定义动画。animator动画XML文件中可以包括以下三种标签:

1)        <animator>:相当于JAVA代码中的ValueAnimator;

2)        <objectAnimator>:相当于JAVA代码中的ObjectAnimator;

3)        <set>:相当于JAVA代码中的AnimatorSet。

如果我们想直接定义一个ValueAnimator,可以这样写:

<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="5000"
    android:interpolator="@android:anim/bounce_interpolator"
    android:repeatCount="2"
    android:repeatMode="restart"
    android:valueFrom="0"
    android:valueTo="100"
    android:valueType="intType" />
如果我们想直接定义一个ObjectAnimator,可以这样写:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="5000"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:propertyName="scaleX"
    android:repeatCount="2"
    android:repeatMode="reverse"
    android:valueFrom="0"
    android:valueTo="360"
    android:valueType="floatType" />
如果我们想要定义一系列动画,可以这样写:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">

    <objectAnimator
        android:duration="5000"
        android:propertyName="translationX"
        android:valueFrom="0"
        android:valueTo="500"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="5000"
        android:propertyName="translationY"
        android:valueFrom="0"
        android:valueTo="500"
        android:valueType="floatType" />

    <set android:ordering="together">
        <objectAnimator
            android:duration="5000"
            android:propertyName="scaleX"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType" />

        <objectAnimator
            android:duration="5000"
            android:propertyName="scaleY"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType" />
    </set>
</set>
在JAVA代码中调用这些XML文件中定义的动画时这样写:
Animator animator = AnimatorInflater.loadAnimator(MainActivity.this,R.animator.object_animator);
animator.setTarget(view);
animator.start();

7      TypeEvaluator

TypeEvaluator的作用是告诉动画如何从初始值过渡到结束值。

我们一直在使用的ObjectAnimator.ofFloat()方法中其实就封装了一个FloatEvaluator,FloatEvaluator是TypeEvaluator的一个子类,可以在指定的float类型的初始值与结束值之间进行平滑的过渡,源码如下:

public class FloatEvaluator implements TypeEvaluator<Number> {
    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

可以看到,FloatEvaluator实现了TypeEvaluator接口并实现了器evaluate方法,在方法中第一个参数是进度,相当于百分比;第二个和第三个参数是开始值和结束值,通过这三个值来确定当前需要的属性值并返回。

前面说过,ValueAnimator和ObjectAnimator除了常用的ofFloat()、ofInt()方法之外,还有一个ofObject()方法,这个方法是对任意对象的属性进行动画操作的,但是Android不知道我们的任意对象是什么对象,因此也不确定我们的属性是怎样从初始值过渡到结束值的,这个时候我们就需要实现一个自己的TypeEvaluator来告诉系统我们的对象的属性是如何过渡的。

例如,我们自定义一个动画,让一个TextView中的文本从“0%”变化到“100%”,核心代码如下:

ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator () {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        int start = Integer.parseInt((String) startValue);
        int end = Integer.parseInt((String) endValue);
        int result = (int) ((start + fraction * (end - start)));
        return result + "%";
    }
}, "0", "100");
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        String result = (String) animation.getAnimatedValue();
        text.setText(result);
    }
});
animator.setDuration(5000);
animator.start();

8      Interpolator

Interpolator的作用是可以控制动画的变化速率。

Android系统中默认为我们提供了多种已经实现好的Interpolator,常用的如下:

1)        BounceInterpolator:弹跳效果;

2)        AccelerateInterpolator:逐渐加速;

3)        DecelerateInterpolator:逐渐减速;

4)        AccelerateDecelerateInterpolator:先加速后减速;

5)        OvershootInterpolator:到达目标点时“跑过头了”,再返回到目标点;

6)        AnticipateInterpolator:移动之前先向后“助跑”;

7)        AnticipateOvershootInterpolator:OvershootInterpolator和AnticipateInterpolator的组合效果;

8)        CycleInterpolator:对于指定的动画,正向做一遍,反响做一遍;

Android中还为我们提供了一个接口TimeInterpolator,供我们自定义插值器。我们也可以实现TimeInterpolator的一些已有实现类(如BaseInterpolator、Interpolator)来自定义插值器。

TimeInterpolator接口中又一个抽象方法setInterpolation(),方法中又一个参数input,这个参数的值在整个动画过程中从0匀速变化到1,也就是相当于一个百分比,指示当前动画播放到哪了。

如果我们在这个方法中直接返回input参数,那么这个插值器就是一个匀速插值器,Android默认给我们提供的LinearInterpolator就是这样写的,源码如下:

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    public float getInterpolation(float input) {
        return input;  //匀速
    }
}
当然,如果我们修改input的值并返回,就会得到加速或减速的效果,如Android默认提供的AccelerateDecelerateInterpolator中的源码:
public class AccelerateDecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;  //先加速后减速
    }
}
有了以上的源码作为参考,我们就可以写自定义的插值器了。例如,我们写一个先减速后加速的插值器DecelerateAccelerateInterpolator,代码如下:
public class DecelerateAccelerateInterpolator implements TimeInterpolator {
    @Override
    public float getInterpolation(float input) {
        float result;
        if (input <= 0.5) {
            result = (float) (Math.sin(Math.PI * input)) / 2;  //前半部分减速
        } else {
            result = (float) (2 - Math.sin(Math.PI * input)) / 2;  //后半部分加速
        }
        return result;
    }
}

9      ViewPropertyAnimator

ViewPropertyAnimator是在Android 3.1的API中新加入的更加易懂、更加便于使用的动画API。

属性动画虽然可以对所有对象添加动画效果,但我们在使用过程中还是以对View使用动画为多,因此Google为我们提供了一个ViewPropertyAnimator,让我们可以简单的调用这套API来单独为View设置动画。

ViewPropertyAnimator让我们可以使用链式编程,一行代码为一个View对象设置属性,示例代码如下:

textView.animate()
        .translationX(200)
        .translationY(200)
        .setDuration(2000)
        .setInterpolator(new BounceInterpolator())
        .start();

当我们对一个View对象使用animate()方法时,就返回了一个ViewPropertyAnimator对象,这个对象中支持所有补间动画的API以及属性动画的API。ViewPropertyAnimator是默认执行的动画,也就是说,我们不需要在最后调用start()方法,动画也会自动播放。

到此为止,属性动画相关的知识点就基本上介绍完了,给自己留个笔记,也希望对大家有帮助~~

参考资料:

ObjectAnimator、ValueAnimator:http://blog.csdn.net/guolin_blog/article/details/43536355

TypeEvaluator:http://blog.csdn.net/guolin_blog/article/details/43816093

Interpolator和ViewPropertyAnimator:http://blog.csdn.net/guolin_blog/article/details/44171115

时间: 2024-08-08 01:16:29

1111111111的相关文章

SpringBoot - 二零一七0421小记

一.SpringBoot使用起来比起SpringMVC更便捷,在注解上的小变化,我记录了下面几个: @Controller + @ResponseBody = SpringMVC中用@RestController来代替前面两个注解,通过这个注解,可以将所有的前端http请求放入SpringBoot的Controller容器中,并返回json格式的数据给前端 @RequestMapping(value={"/hello","/hi"},method=RequestMe

Lisp简明教程

此教程是我花了一点时间和功夫整理出来的,希望能够帮到喜欢Lisp(Common Lisp)的朋友们.本人排版很烂还望多多海涵! <Lisp简明教程>PDF格式下载 <Lisp简明教程>ODT格式下载 具体的内容我已经编辑好了,想下载的朋友可以用上面的链接.本人水平有限,如有疏漏还望之处(要是有谁帮我排排版就好了)还望指出!资料虽然是我整理的,但都是网友的智慧,如果有人需要转载,请至少保留其中的“鸣谢”页(如果能有我就更好了:-)). Lisp简明教程 整理人:Chaobs 邮箱:[

[Linux]-部署Tomcat及其负载均衡

Tomcat 环境:VM CentOS1:192.168.1.1/24 CentOS2:192.168.1.2/24 REHL:192.168.1.3/24 CentOS 1 #yum开始 挂载镜像并连接 [[email protected] ~]# mount /dev/cdrom /mnt/ mount: block device /dev/sr0 is write-protected, mounting read-only 删除原有的yum配置文件,写入自己的 rm -rf /etc/yu

接口测试用例设计实践总结

设计思路 1)   优先级--针对所有接口 1.暴露在外面的接口,因为通常该接口会给第三方调用: 2.供系统内部调用的核心功能接口: 3.供系统内部调用非核心功能接口: 2)   优先级--针对单个接口 1.正向用例优先测试,逆向用例次之(通常情况,非绝对): 2.是否满足前提条件 > 是否携带默认参值参数 > 参数是否必填 > 参数之间是否存在关联 > 参数数据类型限制 >参数数据类型自身的数据范围值限制 3)   设计分析 通常,设计接口测试用例需要考虑以下几个方面: 1

visual_c++外挂教程(详细)

课程分四个大章节 初级篇,中级篇,进阶篇,高级篇 初级篇内容:编写一个完整的,简单的外挂 C++的数据类型:Byte,Word,DWORD,int,float API函数的调mouse_event,GetWindowRect,SetCursorPos,FindWindow,SendMessage) CE5.4工具的使用方法 中级篇内容:调试工具的使用技巧,功能CALL的概念 调试工具OD1.1的使用技巧(如硬件断点,条件断点,内存断点. 常用汇编指令与对应高级语言的转换. 游戏功能CALL概念

08 多态课后作业

知识点一:类型转换 源代码: class Mammal{} class Dog extends Mammal {} class Cat extends Mammal{} public class TestCast { public static void main(String args[]) { Mammal m; Dog d=new Dog(); Cat c=new Cat(); m=d; //d=m;//编译不通过 d=(Dog)m; //d=c;//编译不通过 c=(Cat)m; } }

tyvj1148 小船弯弯

描述 童年的我们,充满了新奇的想法.这天,小朋友们用彩虹画笔在云霞上绘制了世界上最美丽的图画.那描绘的是一条大河波浪宽,风吹稻花香两岸的情景.欣赏着自己的作品,小朋友们别提多开心了.这时,Q小朋友对C小朋友说:你看,河面上那一弯弯船儿,多漂亮啊!可是,这么多船儿,哪一只最大呢?    C小朋友最在意他在Q小朋友心目中的形象了,当然想完美地回答这个问题了.你能帮帮他么?    船的定义为:中间是长宽比例为1:2的矩形,两头是两个等腰直角三角形的等腰梯形.注意:必须保证在船所占据的矩形中除了船的图形

问题 B: 【高精度】简单高精度加法

问题 B: [高精度]简单高精度加法 时间限制: 1 Sec  内存限制: 64 MB提交: 94  解决: 27[提交][状态][讨论版] 题目描述 修罗王解决了计算机的内存限制问题,终于可以使用电脑进行大型的魔法运算了,他交给邪狼的第一个任务是计算两个非负整数A.B的和,其中A和B的位数在5000位以内. 输入 共两行数据,第一行为一个非负整数A,第二行为一个非负整数B,A.B的位数均在5000以内. 输出 输出一个非负数,即两数之和. 样例输入 1111111111 2222222222

JQuery 学习总结及实例 !! (转载)

出自 new:http://www.jianshu.com/users/1967b163cb61/latest_articles 1.JQuery简介 普通JavaScript的缺点:每种控件的操作方式不统一,不同浏览器下有区别,要编写跨浏览器的程序非常麻烦.因此出现了很多对JavaScript的封装库,比如Prototype.Dojo.ExtJS.JQuery等,这些库对JavaScript进行了封装,简化了开发.这些库是对JavaScript的封装,也就是咱们调用JQuery的一句函数,JQ