自定义控件三部曲之动画篇(四)——ValueAnimator基本使用
前言:不要让别人的无知断送了你的梦想,永远坚信你所坚信的。
相关文章:
《Android自定义控件三部曲文章索引》:http://blog.csdn.net/harvic880925/article/details/50995268
一、概述
long long ago,我写过几篇有关Animation的文章,讲解了传统的alpha、scale、translate、rotate的用法及代码生成方法。其实这三篇文章讲的所有动画效果叫做Tween Animation(补间动画)
在Android动画中,总共有两种类型的动画View Animation(视图动画)和Property Animator(属性动画);
其中
- View Animation包括Tween Animation(补间动画)和Frame Animation(逐帧动画);
- Property Animator包括ValueAnimator和ObjectAnimation;
首先,直观上,他们有如下三点不同:
1、引入时间不同:View Animation是API Level 1就引入的。Property Animation是API Level 11引入的,即Android 3.0才开始有Property Animation相关的API。
2、所在包名不同:View Animation在包android.view.animation中。而Property Animation API在包 android.animation中。
3、动画类的命名不同:View Animation中动画类取名都叫XXXXAnimation,而在Property Animator中动画类的取名则叫XXXXAnimator
大
家都知道逐帧动画主要是用来实现动画的,而补间动画才能实现控件的渐入渐出、移动、旋转和缩放的;而Property
Animator是在Android 3.0版本才引入的,之前是没有的。大家可能会觉得补间动画和逐帧动画已经很全了,为什么还要引入Property
Animator呢?
1、为什么引入Property Animator(属性动画)
我提出一个假设:请问大家,如何利用补间动画来将一个控件的背景色在一分钟内从绿色变为红色?这个效果想必没办法仅仅通过改变控件的渐入渐出、移动、旋转和缩放来实现吧,而这个效果是可以通过Property Animator完美实现的
这就是第一个原因:Property Animator能实现补间动画无法实现的功能
大
家都知道,补间动画和逐帧动画统称为View Animation,也就是说这两个动画只能对派生自View的控件实例起作用;而Property
Animator则不同,从名字中可以看出属性动画,应该是作用于控件属性的!正因为属性动画能够只针对控件的某一个属性来做动画,所以也就造就了他能单
独改变控件的某一个属性的值!比如颜色!这就是Property Animator能实现补间动画无法实现的功能的最重要原因。
我们得到了第二点不同:View Animation仅能对指定的控件做动画,而Property Animator是通过改变控件某一属性值来做动画的。
假
设我们将一个按钮从左上角利用补间动画将其移动到右下角,在移动过程中和移动后,这个按钮都是不会响应点击事件的。这是为什么呢?因为补间动画仅仅转变的
是控件的显示位置而已,并没有改变控件本身的值。View Animation的动画实现是通过其Parent
View实现的,在View被drawn时Parents
View改变它的绘制参数,这样虽然View的大小或旋转角度等改变了,但View的实际属性没变,所以有效区域还是应用动画之前的区域;我们看到的效果
仅仅是系统作用在按钮上的显示效果,利用动画把按钮从原来的位置移到了右下角,但按钮内部的任何值是没有变化的,所以按钮所捕捉的点击区域仍是原来的点击
区域。(下面会举例来说明这个问题)
这就得到了第三点不同:补间动画虽能对控件做动画,但并没有改变控件内部的属性值。而Property Animator则是恰恰相反,Property Animator是通过改变控件内部的属性值来达到动画效果的
2、举例说明补间动画的点击区域问题
下面我们就利用TranslateAnimation来做一个移动动画的例子,看它的点击区域是否会变。
我们先来看看效果:
在效果图中,首先,我给textview添加了点击响应,当点击textview时,会弹出Toast。
然后,当我点击按钮的时候,textview开始向右下角移动。
从结果中可以看出,在移动前,点击textview是可以弹出toast的的,在移动后,点击textview时则没有响应,相反,点击textview的原来所在区域则会弹出toast.
这就论证了不同第三点:补间动画虽能对控件做动画,但并没有改变控件内部的属性值
下面简单看看这个动画的实现代码吧:
(1)、看布局(main.xml)
从效果图中也可以看出,布局很简单,一个button,一个textview,垂直排列,布局代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <Button
- android:id="@+id/btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="10dp"
- android:text="start anim"
- />
- <TextView
- android:id="@+id/tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="10dp"
- android:background="#ffff00"
- android:text="Hello qijian"/>
- </LinearLayout>
(2)JAVA代码(MyActivity.java)
接下来是操作代码,就是分别给button和textview添加上点击响应,当点击textview时弹出toast,点击button时,textview使用移动。
代码如下:
- public class MyActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- final TextView tv = (TextView) findViewById(R.id.tv);
- Button btn = (Button)findViewById(R.id.btn);
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- final TranslateAnimation animation = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 400,
- Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 400);
- animation.setFillAfter(true);
- animation.setDuration(1000);
- tv.startAnimation(animation);
- }
- });
- tv.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Toast.makeText(MyActivity.this,"clicked me",Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
这段代码很容易理解,这里就不再细讲了。
二、ValueAnimator简单使用
我们前面讲了Property Animator包括ValueAnimator和ObjectAnimator;这篇文章就主要来看看ValueAnimator的使用方法吧。
我
觉得谷歌那帮老头是最会起名字的人,单从命名上,就能看出来这个东东的含义。ValueAnimator从名字可以看出,这个Animation是针对值
的!ValueAnimator不会对控件做任何操作,我们可以给它设定从哪个值运动到哪个值,通过监听这些值的渐变过程来自己操作控件。以前我们曾讲过
Scroller类,Scroller类也是不会对控件操作的,也是通过给他设定滚动值和时长,它会自己计算滚动过程,然后我们需要监听它的动画过程来自
己操作控件,ValueAnimator的原理与Scroller类相似。有关Scroller的知识,大家可以参考:《 ListView滑动删除实现之四——Scroller类与listview缓慢滑动》
1、初步使用ValueAnimator
要使用ValueAnimaiton,总共有两步:
第一步:创建ValueAnimator实例
- ValueAnimator animator = ValueAnimator.ofInt(0,400);
- animator.setDuration(1000);
- animator.start();
在
这里我们利用ValueAnimator.ofInt创建了一个值从0到400的动画,动画时长是1s,然后让动画开始。从这段代码中可以看
出,ValueAnimator没有跟任何的控件相关联,那也正好说明ValueAnimator只是对值做动画运算,而不是针对控件的,我们需要监听
ValueAnimator的动画过程来自己对控件做操作。
第二步:添加监听
上面的三行代码,我们已经实现了动画,下面我们就添加监听:
- ValueAnimator animator = ValueAnimator.ofInt(0,400);
- animator.setDuration(1000);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int curValue = (int)animation.getAnimatedValue();
- Log.d("qijian","curValue:"+curValue);
- }
- });
- animator.start();
在上面的代码中,我们通过addUpdateListener添加了一个监听,在监听传回的结果中,是表示当前状态的ValueAnimator实例,我们通过animation.getAnimatedValue()得到当前值。然后通过Log打印出来,结果如下:
这就是ValueAnimator的功能:ValueAnimator对指定值区间做动画运算,我们通过对运算过程做监听来自己操作控件。
总而言之就是两点:
- ValueAnimator只负责对指定的数字区间进行动画运算
- 我们需要对运算过程进行监听,然后自己对控件做动画操作
2、实例使用ValueAnimator
这段,我们就使用上面我们讲到的ValueAnimator做一个动画:
我们先看看效果图:
首先这个动画的布局与上一个实例是一样的。但实现的效果确不大相同:
- 首先,点击按钮后,textview从屏幕(0,0)点运动到(400,400)点
- 运动前后,textview都是可以响应点击事件的
下面我们就来看看这里如何利用ValueAnimator来实现这个效果的。
1、布局(main.xml)
布局代码与上个例子相同:垂直布局按钮控件和textview
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <Button
- android:id="@+id/btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="10dp"
- android:text="start anim"
- />
- <TextView
- android:id="@+id/tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="10dp"
- android:background="#ffff00"
- android:text="Hello qijian"/>
- </LinearLayout>
2、JAVA操作
首先,是对textview和btn添加点击响应,当点击textview时,弹出toast;点击btn时,textview开始做动画
- public class MyActivity extends Activity {
- private TextView tv;
- private Button btn;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- tv = (TextView) findViewById(R.id.tv);
- btn = (Button) findViewById(R.id.btn);
- btn.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- doAnimation();
- }
- });
- tv.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Toast.makeText(MyActivity.this, "clicked me", Toast.LENGTH_SHORT).show();
- }
- });
- }
- …………
- }
这段代码很简单,在点击btn的时候执行 doAnimation()来执行动画操作,在点击tv的时候,弹出toast;
下面来看看 doAnimation()的具体实现:
- private void doAnimation(){
- ValueAnimator animator = ValueAnimator.ofInt(0,400);
- animator.setDuration(1000);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int curValue = (int)animation.getAnimatedValue();
- tv.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());
- }
- });
- animator.start();
- }
首先,我们构造一个ValueAnimator实例,让其计算的值是从0到400;
然后添加对计算过程进行监听:
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int curValue = (int)animation.getAnimatedValue();
- tv.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());
- }
- });
在
监听过程中,通过layout函数来改变textview的位置。这里注意了,我们是通过layout函数来改变位置的,我们知道layout函数在改变
控件位置时是永久性的,即通过更改控件left,top,right,bottom这四个点的坐标来改更改坐标位置的,而不仅仅是从视觉上画在哪个位置,
所以通过layout函数更改位置后,控件在新位置是可以响应点击事件的。
大家可能注意到了,layout()函数中上下左右点的坐标是以屏幕坐标来标准的。所以在效果图中可以看到,textview的运动轨迹是从屏幕的左上角(0,0)点运行到(400,400)点。
三、常用方法
经过上面的例子,我们就大概知道ValueAnimator要怎么使用了,下面我们就来具体来看看它还有哪些常用的方法吧。
1、ofInt与ofFloat
在上面的例子中,我们使用了ofInt函数,与它同样功能的还有一个函数叫ofFloat,下面我们先看看他们的具体声明:
- public static ValueAnimator ofInt(int... values)
- public static ValueAnimator ofFloat(float... values)
他
们的参数类型都是可变参数长参数,所以我们可以传入任何数量的值;传进去的值列表,就表示动画时的变化范围;比如ofInt(2,90,45)就表示从数
值2变化到数字90再变化到数字45;所以我们传进去的数字越多,动画变化就越复杂。从参数类型也可以看出ofInt与ofFloat的唯一区别就是传入
的数字类型不一样,ofInt需要传入Int类型的参数,而ofFloat则表示需要传入Float类型的参数。
下面我们还在上面例子的基础上,使用ofFloat函数来举个例子:
- ValueAnimator animator = ValueAnimator.ofFloat(0f,400f,50f,300f);
- animator.setDuration(3000);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- Float curValueFloat = (Float)animation.getAnimatedValue();
- int curValue = curValueFloat.intValue();
- tv.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());
- }
- });
- animator.start();
先看看效果:
在效果图中,我们可以看到,在点击按钮之后,textview先向右下运动然后再回来,然后再向右下运动过去
在这个例子中,我们使用ValueAnimator.ofFloat(0f,400f,50f,300f)构造了一个比较复杂的动画渐变,值是0变到400再回到50最后变成300;
所以我们在监听时,首先得到当前动画的值:
- Float curValueFloat = (Float)animation.getAnimatedValue();
通过getAnimatedValue()来获取当前运动点的值,大家可能会疑问为什么要转成Float类型,我们先来看看getAnimatedValue()的声明:
- Object getAnimatedValue();
它
返回的类型是一个Object原始类型,那我们怎么知道我们要将它强转成什么类型呢。注意,我们在设定动画初始值时用的是ofFloat()函数,所以每
个值的类型必定是Float类型,所以我们获取出来的类型也必然是Float类型的。同样,如果我们使用ofInt设定的初始值,那么通过
getAnimatedValue()获取到的值就应该强转为Int类型。
在得到当前运动的值以后,通过layout函数将textview移动到指定位置即可。
2、常用函数
先做个汇总,这部分将讲述的方法有:
- /**
- * 设置动画时长,单位是毫秒
- */
- ValueAnimator setDuration(long duration)
- /**
- * 获取ValueAnimator在运动时,当前运动点的值
- */
- Object getAnimatedValue();
- /**
- * 开始动画
- */
- void start()
- /**
- * 设置循环次数,设置为INFINITE表示无限循环
- */
- void setRepeatCount(int value)
- /**
- * 设置循环模式
- * value取值有RESTART,REVERSE,
- */
- void setRepeatMode(int value)
- /**
- * 取消动画
- */
- void cancel()
1、setDuration()、getAnimatedValue()、start()
这三个函数在上面的实例中已经使用过,setDuration(long duration)是设置一次动画的时长,单位是毫秒,start()是开始动画,唯一有点难度的是Object getAnimatedValue(),它的声明为:
- Object getAnimatedValue();
它
的意义就是获取动画在当前运动点的值,所以这个对象只能用于在动画运动中。返回的值是Object,上面我们说过,通过
getAnimatedValue()得到的值的实际类型与初始设置的值相同,如果我们利用ofInt()设置的动画,那通过
getAnimatedValue()得到的值为类型就是Int类型。如果我们利用ofFloat()设置的动画,通过
getAnimatedValue()得到的值类型就是Float类型。
总而言之,通过getAnimatedValue()值类型与初始设置动画时的值类型相同
上面我们已经用过这些函数了,这里就不再举例了。
2、setRepeatCount()、setRepeatMode()、cancel()
setRepeatCount(int value)用于设置动画循环次数,设置为0表示不循环,设置为ValueAnimation.INFINITE表示无限循环。
cancel()用于取消动画
我们着重说一下setRepeatMode:
- /**
- * 设置循环模式
- * value取值有RESTART,REVERSE
- */
- void setRepeatMode(int value)
setRepeatMode(int value)用于设置循环模式,取值为ValueAnimation.RESTART时,表示正序重新开始,当取值为ValueAnimation.REVERSE表示倒序重新开始。
下面我们使用这三个函数来举个例子,先看下动画效果:
在
这里,有两个按钮,当点击start
anim时,textview垂直向下运动,我定义的运动初始值为ofInt(0,400);所以从效果图中也可以看出我们定义它为无限循环,而且每次循
环时都是使用ValueAnimation.REVERSE让其倒序重新开始循环。当我们点击cancel anim时,取消动画。
下面我们来看看代码
首先是布局代码,布局代码时,采用RelativeLayout布局,将两个按钮放两边,textview放中间,代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <Button
- android:id="@+id/btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:padding="10dp"
- android:text="start anim"
- />
- <Button
- android:id="@+id/btn_cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:padding="10dp"
- android:text="cancel anim"
- />
- <TextView
- android:id="@+id/tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:padding="10dp"
- android:background="#ffff00"
- android:text="Hello qijian"/>
- </RelativeLayout>
这个布局代码没什么难度就不讲了。
下面来看看两个按钮的操作代码:
- private Button btnStart,btnCancel;
- private ValueAnimator repeatAnimator;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- tv = (TextView) findViewById(R.id.tv);
- btnStart = (Button) findViewById(R.id.btn);
- btnCancel = (Button)findViewById(R.id.btn_cancel);
- btnStart.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- repeatAnimator = doRepeatAnim();
- }
- });
- btnCancel.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- repeatAnimator.cancel();
- }
- });
- }
这
段代码也没什么难度,当我们点击btnStart的时候,执行doRepeatAnim()函数,这个函数返回它构造的ValueAnimator对象,
将其赋值给repeatAnimator变量。当点击btnCancel时,调用 repeatAnimator.cancel()取消当前动画。
下面我们来看看doRepeatAnim()函数都做了哪些工作:
- private ValueAnimator doRepeatAnim(){
- ValueAnimator animator = ValueAnimator.ofInt(0,400);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int curValue = (int)animation.getAnimatedValue();
- tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());
- }
- });
- animator.setRepeatMode(ValueAnimator.REVERSE);
- animator.setRepeatCount(ValueAnimator.INFINITE);
- animator.setDuration(1000);
- animator.start();
- return animator;
- }
在
这里我们构造了一个ValueAnimator,动画范围是0-400,设置重复次数为无限循环。循环模式为倒序。在
animator.setDuration(1000)表示动画一次的时长为1000毫秒。最后,由于我们在取消动画时还需要我们构造的这个
ValueAnimator实例,所以将animator返回。
3、两个监听器
(1)、添加监听器
前面,我们讲过一个添加监听器animator.addUpdateListener,以监听动画过程中值的实时变化,其实在ValueAnimator中共有两个监听器:
- /**
- * 监听器一:监听动画变化时的实时值
- */
- public static interface AnimatorUpdateListener {
- void onAnimationUpdate(ValueAnimator animation);
- }
- //添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
- /**
- * 监听器二:监听动画变化时四个状态
- */
- public static interface AnimatorListener {
- void onAnimationStart(Animator animation);
- void onAnimationEnd(Animator animation);
- void onAnimationCancel(Animator animation);
- void onAnimationRepeat(Animator animation);
- }
- //添加方法为:public void addListener(AnimatorListener listener)
关
于监听器一:AnimatorUpdateListener就是监听动画的实时变化状态,在
onAnimationUpdate(ValueAnimator
animation)中的animation表示当前状态动画的实例。这里就不再细讲这个监听器了,这里我们主要讲讲监听器
AnimatorListener;
在AnimatorListener中,主要是监听Animation的四个状态,start、end、
cancel、repeat;当动画开始时,会调用onAnimationStart(Animator
animation)方法,当动画结束时调用onAnimationEnd(Animator
animation),当动画取消时,调用onAnimationCancel(Animator
animation)函数,当动画重复时,会调用onAnimationRepeat(Animator animation)函数。
添加AnimatorListener的方法是addListener(AnimatorListener listener) ;
下面我们就举个例子来看一下AnimatorListener的使用方法。
我们在上面doRepeatAnim()函数的基础上,添加上AnimatorListener,代码如下:
代码如下:
- private ValueAnimator doAnimatorListener(){
- ValueAnimator animator = ValueAnimator.ofInt(0,400);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int curValue = (int)animation.getAnimatedValue();
- tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());
- }
- });
- animator.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- Log.d("qijian","animation start");
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- Log.d("qijian","animation end");
- }
- @Override
- public void onAnimationCancel(Animator animation) {
- Log.d("qijian","animation cancel");
- }
- @Override
- public void onAnimationRepeat(Animator animation) {
- Log.d("qijian","animation repeat");
- }
- });
- animator.setRepeatMode(ValueAnimator.REVERSE);
- animator.setRepeatCount(ValueAnimator.INFINITE);
- animator.setDuration(1000);
- animator.start();
- return animator;
- }
在上面的代码中,我们是在doRepeatAnim()函数的基础上,又添加了AnimatorListener()以监听它的状态,并把这些状态打印出来。
我们来看看动画效果:
打印出来结果如下:
(2)、取消监听
上面我们讲了如何添加监听函数,下面我们来看看如何移除监听器:
- /**
- * 移除AnimatorUpdateListener
- */
- void removeUpdateListener(AnimatorUpdateListener listener);
- void removeAllUpdateListeners();
- /**
- * 移除AnimatorListener
- */
- void removeListener(AnimatorListener listener);
- void removeAllListeners();
针
对AnimatorUpdateListener和AnimatorListener,每个监听器都有两个方法来移除;我们就以移除
AnimatorListener来简单讲一下,removeListener(AnimatorListener
listener)用于在animator中移除指定的监听器,而removeAllListeners()用于移除animator中所有的
AnimatorListener监听器;
下面上在添加监听器的例子基础上,不改变doAnimatorListener()的代码,仍然是textview做动画时添加AnimatorListener的状态监听。然后点击cancelAnim时,移除AnimatorListener,代码如下:
AnimatorListener的代码:
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- …………
- btnStart.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- repeatAnimator = doAnimatorListener();
- }
- });
- btnCancel.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- repeatAnimator.removeAllListeners();
- }
- });
- }
doAnimatorListener
的代码与上面的一样,就不再重复贴了,当点击btnCancel时移除animator中所有的AnimatorListener,但注意的是,我们在移
除AnimatorListener后,并没有cancel动画效果,所以动画会一直不停的运动下去。但移除AnimatorListener之
后,Log应该就不会再打印了。
效果如下:
在效果图中,在动画循环了三次之后,我们点击btnCancel移除所有的AnimatorListener;打印tag如下:
可见只打印了循环三次以前的log,在移除我们添加的AnimatorListener之后,我们打印log的代码就不会再执行了,所以也就不会再有log了。
好了,有关监听器的部分,我们就到这里了
4、其它函数
上面我们讲了ValueAnimator中常用的一些函数,但是还有一些函数虽然不常用,但我们还是简单讲一下,他们分别是:
- /**
- * 延时多久时间开始,单位是毫秒
- */
- public void setStartDelay(long startDelay)
- /**
- * 完全克隆一个ValueAnimator实例,包括它所有的设置以及所有对监听器代码的处理
- */
- public ValueAnimator clone()
setStartDelay(long startDelay)非常容易理解,就是设置多久后动画才开始。
但clone()这个函数就有点难度了;首先是什么叫克隆。就是完全一样!注意是完全一样!就是复制出来一个完全一样的新的ValueAnimator实例出来。对原来的那个ValueAnimator是怎么处理的,在这个新的实例中也是全部一样的。
我们来看一个例子来看一下,什么叫全部一样:
首先,我们定义一个函数doRepeatAnim():
- private ValueAnimator doRepeatAnim(){
- ValueAnimator animator = ValueAnimator.ofInt(0,400);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- int curValue = (int)animation.getAnimatedValue();
- tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());
- }
- });
- animator.setDuration(1000);
- animator.setRepeatMode(ValueAnimator.REVERSE);
- animator.setRepeatCount(ValueAnimator.INFINITE);
- return animator;
- }
这
个函数其实与上面在讲循环函数时的doRepeatAnim()函数是一样的;在这个函数中,我们定义一个ValueAnimator,设置为无限循环,
然后添加AnimatorUpdateListener监听;在动画在运动时,向下移动textview.这里要非常注意的一点是我们只是定义了一个
ValueAnimator对象,并没有调用start()让动画开始!!!!
然后我们再看看点击btnStart和btnCancel时的代码处理:
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- …………
- btnStart.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- repeatAnimator = doRepeatAnim();
- //克隆一个新的ValueAnimator,然后开始动画
- ValueAnimator newAnimator = repeatAnimator.clone();
- newAnimator.setStartDelay(1000);
- newAnimator.start();
- }
- });
- btnCancel.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- repeatAnimator.removeAllUpdateListeners();
- repeatAnimator.cancel();
- }
- });
- }
在上面的代码中,我们在点击btnStart时:
- repeatAnimator = doRepeatAnim();
- //克隆一个新的ValueAnimator,然后开始动画
- ValueAnimator newAnimator = repeatAnimator.clone();
- newAnimator.setStartDelay(1000);
- newAnimator.start();
我们利用clone()克隆了一个doRepeatAnim()生成的对象。然后调用setStartDelay(1000);将动画开始时间设为1000毫秒后开始动画。最后调用start()函数开始动画。
这
里有一点非常注意是:我们除了对newAnimator设置了动画开始延时1000毫秒以后,没有对它进行任何设置,更没有在在它的监听器中对
textview的处理!!!!那textview会动吗?答案是会动的,我们讲了,克隆就是完全一样,在原来的ValueAnimator中是如何处理
的,克隆过来的ValueAnimator也是完全一样的处理方式!
在点击btnCancel时:
- repeatAnimator.removeAllUpdateListeners();
- repeatAnimator.cancel();
我
们既移除了repeatAnimator的监听器又取消了动画。但有用吗?必须当然是没用的,因为我们start的动画对象是从
repeatAnimator克隆来的newAnimator。这好比是克隆羊,原来的羊和克隆羊什么都是一样的,但你把原来的羊杀了,克隆的羊会死吗?
用大脚指头想都知道不会!所以如果要取消当前的动画必须通过newAnimator.cancel()来取消
效果图如下:
从效果图中也可以看出,点击btnCancel按钮是没有做用的,并没能取消动画。
好了,到这里有关ValueAnimator的常用函数基本就讲完了,下篇将更深入的讲解ValueAnimator的高级用法。
源码内容:
1、《try_tween_anim_click》:对应概述中:举例说明补间动画的点击区域问题
2、《BlogValueAnimator1》:对应:《实例使用ValueAnimator》和《ofInt与ofFloat》两部分源码
3、《BlogValueAnimator2》:对应以后所有的源码
如果本文有帮到你,记得加关注哦
源码地址:
csdn:http://download.csdn.net/detail/harvic880925/9405988
github:https://github.com/harvic/BlogResForGitHub
请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/50525521
自定义控件三部曲之动画篇(四)——ValueAnimator基本使用的更多相关文章
- 自定义控件三部曲之动画篇(一)——alpha、scale、translate、rotate、set的xml属性及用法
前言:这几天做客户回访,感触很大,用户只要是留反馈信息,总是一种恨铁不成钢的心态,想用你的app,却是因为你的技术问题,让他们不得不放弃,而你一个回访电话却让他们尽释前嫌,当最后把手机号留给他们以便随 ...
- 转:自定义控件三部曲之动画篇——alpha、scale、translate、rotate、set的xml属性及用法
第一篇: 一.概述 Android的animation由四种类型组成:alpha.scale.translate.rotate,对应android官方文档地址:<Animation Resour ...
- 自己定义控件三部曲之动画篇(七)——ObjectAnimator基本使用
前言: 假如生活欺骗了你, 不要悲伤,不要心急! 忧郁的日子里须要镇静: 相信吧,快乐的日子终将会来临! 心儿永远向往着未来: 如今却常是忧郁. 一切都是瞬息,一切都将会过去: 而那过去了的,就会成为 ...
- 自己定义控件三部曲之动画篇(十三)——实现ListView Item进入动画
前言:宝剑锋从磨砺出,梅花香自苦寒来 相关文章: <Android自己定义控件三部曲文章索引>: http://blog.csdn.net/harvic880925/article/det ...
- [转]Android自定义控件三部曲系列完全解析(动画, 绘图, 自定义View)
来源:http://blog.csdn.net/harvic880925/article/details/50995268 一.自定义控件三部曲之动画篇 1.<自定义控件三部曲之动画篇(一)—— ...
- [转]ANDROID L——Material Design详解(动画篇)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 转自:http://blog.csdn.net/a396901990/article/de ...
- iOS 开发之动画篇 - 从 UIView 动画说起
毋庸置疑的:在iOS开发中,制作动画效果是最让开发者享受的环节之一.一个设计严谨.精细的动画效果能给用户耳目一新的效果,吸引他们的眼光 —— 这对于app而言是非常重要的. 本文作为动画文集的第一篇, ...
- ANDROID L——Material Design具体解释(动画篇)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Android L: Google已经确认Android L就是Android Lolli ...
- 【基于WPF+OneNote+Oracle的中文图片识别系统阶段总结】之篇四:关于OneNote入库处理以及审核
篇一:WPF常用知识以及本项目设计总结:http://www.cnblogs.com/baiboy/p/wpf.html 篇二:基于OneNote难点突破和批量识别:http://www.cnblog ...
随机推荐
- 【PostgreSQL-9.6.3】表操作语句
1.创建数据表 create table table_name ( 字段1 数据类型[列级别约束条件][默认值], 字段2 数据类型[列级别约束条件][默认值], 字段3 数据类型[列级别约束条件][ ...
- java Web(4)
Web 应用程序状态管理 通过隐藏表单域 hidden,cookie,session,重写URL来实现: cookie存在于客户端,浏览器关闭时失效 cookie原理:服务器在响应请求时将一些数据以“ ...
- SLAM: 图像角点检测的Fast算法(OpenCV文档)
官方链接:http://docs.opencv.org/trunk/doc/py_tutorials/py_feature2d/py_fast/py_fast.html#fast-algorithm- ...
- XML-20100: (Fatal Error) Expected ';'. xml转word 导出时异常
因为数据中包含特殊字符.需要进行转译. < < 小于号 > > 大于号 & & 和 ' ’ 单引号 " " 双引号 XML ...
- HTML5 video常用属性
一.视频video常用标签方法 <!-- controls 控制条,播放暂停等 controlslist 控制不允许全屏 不允许下载等 poster 封面 autoplay 自动播放 muted ...
- STM32F103 rtthread工程构建
目录 STM32F103 工程构建 1.基本情况 2.硬件连接 3.rtthread配置 4.点灯 5. 码云上git操作 STM32F103 工程构建 1.基本情况 RAM 20K ROM 64K ...
- eas之获得任何一个KDTable的选中行
import com.kingdee.bos.ctrl.kdf.table.util.KDTableUtil; int[] selectRows =KDTableUtil.getSelectedRow ...
- 【剑指Offer】51、构建乘积数组
题目描述: 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1]. 其中B中的元素B[i]=A[0] * A[1]... * A[i-1] * A[i+1] ...
- 分别用for循环,while do-while以及递归方法实现n的阶乘!
分别用for循环,while do-while以及递归方法实现n的阶乘! 源码: package book;import java.util.Scanner;public class Access { ...
- 关于swift 底部工具栏图标锯齿模糊问题。
今天在底部工具栏添加图片时发现图片模糊而且有锯齿,开始一直以为是美工给的图片有问题,后来发现是要设置两种图片: 比如 index.png(默认30 * 30),indexSelected(选中后的图 ...