第七章Andorid动画深入分析(Android开发艺术探索)
2017-03-16 15:38
309 查看
Android 平台提供了一套完整的动画框架
在Android3.0之前有两种动画,一种方式是补间动画 Tween Animation、另一种叫逐帧动画 Frame Animation(也称Drawable Animation );
Android3.0以后增加了属性动画 Property Animation
Tween Animation、Frame Animation只能用于View,被归类为View
Animation。
所以Andorid的动画分为2种:
View动画(View Animation)、属性动画(Property Animation)
7.1、View动画
7.1.1、View动画的种类
View动画的四种变换效果对应着Animation的四个子类:
要使用view的动画,首先要在res/anim下面创建动画xml
基本属性如下:
![](https://img-blog.csdn.net/20170316153528785?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI3Nzc0MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
标签表示动画集合,对应AnimitionSet类,view可以是单个动画,也可以是多个动画的组合
它的两个属性:
android:interpolater:默认是@android:anim/accelerate_interpolator即加速减速插值器
android:shareInterpolater:表示集合中的动画是否和集合用一样的插值器,默认为true
1、TranslateAnimation(平移):
常用属性如下:
2、ScaleAnimatino(缩放)
常用属性如下:
注意:轴点默认在View的中心,这个时候横向缩放的话,左右都会缩放、纵向的话,上下都会缩放
这个时候可以设置轴点的位置,如果在最左边那么只会缩放右边,如果在最右边那么只会缩放左边
3、RotateAnimation(旋转)
常用属性如下:
注意:轴点默认在View的中心,即view的旋转中心,围绕着view中心点和view左上角,效果明显是不一样的
4、AlphaAnimation(透明)
常用属性如下:
每个动画都有的公共常用属性:
拓展:
下面来一个例子:
res/drawable/translate
res/drawable/scale
res/drawable/rotate
res/drawable/alpha
布局文件:
Activity:
效果图如下:
![](https://img-blog.csdn.net/20170316153613466?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI3Nzc0MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
除了xml中定义的动画外,还可以通过代码来应用动画
如:
其他动画同理
拓展:
7.1.3、自定义View动画
实际开发中很少自定义view动画,只需要知道是矩阵变换的过程就好
7.1.4、帧动画
帧动画是顺序播放一组预定好的图片
Andorid系统为我们提供了一个类:AnimationDrawable
使用步骤:
1、定义一个xml
2、让其作为view的背景即可
帧动画使用比较简单,但是容易引起oom,所以使用帧动画的时候避免使用尺寸较大的图片
7.2、View动画的特殊使用场景
7.2.1、LayoutAnimation
LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,常常被用在listview,recycleview上面,给ViewGroup指定一个动画,那么它的子元素就也有了这个动画
为了给viewGroup子元素加上出场效果,遵循如下几个步骤:
1、定义LayoutAnimation
2、为子元素指定具体的入场
3、为ViewGroup指定android:layoutAnimation
除了在XML中定义,我们还可以在代码中定义
7.2.2、Activity的切换效果
通常我们使用:
在finish的时候:
注意:overridePendingTransition必须在startActivity或者finish的后面
也可以在style.xml中设置
7.3、属性动画
7.3.1、使用属性动画
属性动画是3.0以后引入的新动画,
常用的动画类有:ValueAnimator、ObjectAnimator、AnimatorSet
Property Animation可以定义在xml文件中
这些xml文件定义的文件路径如下: res/animator/filename.xm
基本用法如下:
文件需要有根元素,可以使用, , or . 可以作为一个集合,而且集合中还可以存在元素。
同样也可以通过代码进行设置:
ObjectAnimator的使用:
ValueAnimator 的使用
AnimatorSet 的使用
ValueAnimator是Property Animation系统的核心类,它包含了大部分api
objectAnimator继承ValueAnimator,是动画的拓展类
属性动画基本使用如下:
使用如下代码调用:
在实际开发中我们建议用代码的实现属性动画,因为代码实现比较简单,更加重要的是
很多时候起始值是无法确定的,需要动态的创建动画,更加灵活
7.3.2、理解插值器和估值器
这里我们将了解2个知识点:Interpolate(插值器)和TypeEvaluator(估值器)
常用的插值器如下:
![](https://img-blog.csdn.net/20170316153706035?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI3Nzc0MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
如果系统提供的插值器无法满足我们的需求,我们还可以自定义插值器:
步骤很简单:
步骤一:继承TimeInterpolator
步骤二:重写getInterpolation(float input)方法
代码如下:
7.3.3、属性动画的监听器
Property Animation提供了
Animator.AnimatorListener和Animator.AnimatorUpdateListener两个监听器用于动画在播放过程中的重要动画事件。
下面是两个监听器接口和方法的一些介绍和说明:
Animator.AnimatorListen
onAnimationStart() —— 动画开始时调用;
onAnimationEnd() —— 动画结束时调用;
onAnimationRepeat() —— 动画循环播放时调用;
onAnimationCancel() —— 动画被取消时调用。不管终止的方式如何,被取消的动画仍然会调onAnimationEnd();
Animator.AnimatorUpdateListener:
onAnimationUpdate() —— 动画每播放一帧时调用。在动画过程中,可侦听此事件来获取并使用 ValueAnimator 计算出来的属性值。
利用传入事件的 ValueAnimator 对象,调用其 getAnimatedValue() 方法即可获取当前的属性值。如果使用 ValueAnimator来实现动画的话 ,则必需实现此侦听器。
7.3.4、对任意属性做动画
这里先提出一个问题:
给button加个动画,宽度从当前宽度增加200px,也许你会说用view动画搞定,但是当你做的时候你会发现view动画不支持对宽度进行动画,不过可以使用缩放动画试试:
效果如下:
![](https://img-blog.csdn.net/20170316153725957?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzI3Nzc0MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
这样的效果当然不是我们想要的效果,文本都被拉伸了。
下面我们使用属性动画来试试:
很显然效果达到了
拓展:
代码如下:
关于属性动画更多的介绍请参照
Andorid属性动画完全解析上、中、下
http://blog.csdn.net/guolin_blog/article/details/43536355
http://blog.csdn.net/guolin_blog/article/details/43816093
http://blog.csdn.net/guolin_blog/article/details/44171115
Andorid属性动画完全解析上、下
http://blog.csdn.net/lmj623565791/article/details/38067475
http://blog.csdn.net/lmj623565791/article/details/38092093
其他
http://blog.csdn.net/yegongheng/article/details/38435553
http://blog.csdn.net/xyz_lmn/article/details/38667899
拓展:
看了这么多例子,我不知道那些系统的propertyName一共有哪些,我看了下ObjectAnimator源码才发现,下面贴出来:
有了这些,你传入对应的propertyName,就可以实现各种各样的功能啦
7.3.5、属性动画的工作原理
属性动画要求动画作用的对象提供该属性的set方法,属性动画根据你提供的开始值和最终值,多次调用set方法,每次传的值都不一样。
下面我们看源码分析:
1、我们通常使用都是ObjectAnimator.ofInt().start();
那我们就从start()方法看看,
代码看似很多,其实做的事情很简单
首先判断
如果当前动画(mAnimations)、等待的动画(mPendingAnimations)、延迟动画(mDelayedAnims)它们之中只要有和当前动画相同的动画(通过instanceof 进行比较),那么就调用 anim.cancel();来取消当前的动画;
接下来是一段LOG日志
最后如果都不重复的话就直接调用父类的启动动画方法了(super.start());
因为ObjectAnimator继承ValueAnimator那么我们再来看看ValueAnimator的start()方法
通过上述代码,我们可以看出,属性动画需要运行在looper的线程中,最终会调用 animationHandler.start();
然后调用到JNI层,最后回调回来。
这边有一个核心方法:doAnimationFrame
其中animationFrame内部调用animateValue
在初始化的时候如果属性没有提供初始值,则get方法将被会调用,我们看PropertyValuesHolder的setValue方法
可以看到get方法是通过反射调用的,当动画到下一帧的时候,调用setAnimatedValue将新的属性设置给对象,调用set方法。
set方法也是通过反射调用的
代码如下:
在Android3.0之前有两种动画,一种方式是补间动画 Tween Animation、另一种叫逐帧动画 Frame Animation(也称Drawable Animation );
Android3.0以后增加了属性动画 Property Animation
Tween Animation、Frame Animation只能用于View,被归类为View
Animation。
所以Andorid的动画分为2种:
View动画(View Animation)、属性动画(Property Animation)
7.1、View动画
7.1.1、View动画的种类
View动画的四种变换效果对应着Animation的四个子类:
TranslateAnimation(平移<transalte>) ScaleAnimatino(缩放<scale>) RotateAnimation(旋转<rotate>) AlphaAnimation(透明<alpha>)
要使用view的动画,首先要在res/anim下面创建动画xml
基本属性如下:
标签表示动画集合,对应AnimitionSet类,view可以是单个动画,也可以是多个动画的组合
它的两个属性:
android:interpolater:默认是@android:anim/accelerate_interpolator即加速减速插值器
android:shareInterpolater:表示集合中的动画是否和集合用一样的插值器,默认为true
1、TranslateAnimation(平移):
常用属性如下:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true" android:zAdjustment="normal" > <translate //持续的时间 android:duration="100" //x的起始值 android:fromXDelta="0" //Y的起始值 android:fromYDelta="0" //插值器 android:interpolator="@android:anim/linear_interpolator" //x的结束值 android:toXDelta="100" //Y的结束值 android:toYDelta="100" /> </set>
2、ScaleAnimatino(缩放)
常用属性如下:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="300" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true" > <scale //水平方向的缩放起始值 android:fromXScale="0.5" //竖直方向的缩放起始值 android:fromYScale="0.5" //水平方向的缩放结束值 android:toXScale="1.2" //竖直方向的缩放结束值 android:toYScale="1.2" //缩放的轴点的X坐标 android:pivotX="100" //缩放的轴点的Y坐标 android:pivotY="100"/> </set>
注意:轴点默认在View的中心,这个时候横向缩放的话,左右都会缩放、纵向的话,上下都会缩放
这个时候可以设置轴点的位置,如果在最左边那么只会缩放右边,如果在最右边那么只会缩放左边
3、RotateAnimation(旋转)
常用属性如下:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="300" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true" > <rotate //旋转开始的角度 android:fromDegrees="0" //旋转结束的角度 android:toDegrees="90" /旋转的轴点的X坐标 android:pivotX="100" //旋转的轴点的Y坐标 android:pivotY="100" /> </set>
注意:轴点默认在View的中心,即view的旋转中心,围绕着view中心点和view左上角,效果明显是不一样的
4、AlphaAnimation(透明)
常用属性如下:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="300" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true" > <alpha //透明度的起始值 android:fromAlpha="0.1" //透明度的结束值 android:toAlpha="1" /> </set>
每个动画都有的公共常用属性:
动画的持续时间: andorid:duration = "100" 动画结束以后是否停留在结束位置 andorid:fillAtler = "true",true表示停留,false表示不停留
拓展:
fillBefore是指动画结束时画面停留在此动画的第一帧; fillAfter是指动画结束是画面停留在此动画的最后一帧。 Java代码设置如下: /*****动画结束时,停留在最后一帧********* setFillAfter(true); setFillBefore(false); /*****动画结束时,停留在第一帧********* setFillAfter(false); setFillBefore(true); xml设置如下: /******动画结束时,停留在最后一帧********** <set Android:fillAfter="true" android:fillBefore="false"> /******动画结束时,停留在第一帧********** <set android:fillAfter="false" android:fillBefore="true"> PS:xml设置在scale标签里面设置是无效的,注意是set标签
下面来一个例子:
res/drawable/translate
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true"> <translate android:duration="100" android:fromXDelta="0" android:fromYDelta="0" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="100" android:toYDelta="100" /> </set>
res/drawable/scale
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true"> <scale android:fromXScale="0.5" android:fromYScale="0.5" android:pivotX="100" android:pivotY="100" android:toXScale="1.2" android:toYScale="1.2" /> </set>
res/drawable/rotate
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true" > <rotate android:fromDegrees="0" android:toDegrees="90" android:pivotX="100" android:pivotY="100" /> </set>
res/drawable/alpha
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true"> <alpha android:fromAlpha="0.1" android:toAlpha="1" /> </set>
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.zx.eventbus.AnimationActivity"> <Button android:id="@+id/btn_tanslate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="让我们移动" /> <Button android:id="@+id/btn_scale" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="让我们缩放" /> <Button android:id="@+id/btn_rotate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="让我们旋转" /> <Button android:id="@+id/btn_alpha" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="让我们透明" /> </LinearLayout>
Activity:
package com.zx.eventbus; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.Button; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; public class AnimationActivity extends AppCompatActivity { @Bind(R.id.btn_tanslate) Button btnTanslate; @Bind(R.id.btn_scale) Button btnScale; @Bind(R.id.btn_rotate) Button btnRotate; @Bind(R.id.btn_alpha) Button btnAlpha; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_animation); ButterKnife.bind(this); } private void showAnimation(int anim, Button button) { Animation animation = AnimationUtils.loadAnimation(this, anim); button.startAnimation(animation); } @OnClick({R.id.btn_tanslate, R.id.btn_scale, R.id.btn_rotate, R.id.btn_alpha}) public void onClick(View view) { switch (view.getId()) { case R.id.btn_tanslate: showAnimation(R.anim.translate, btnTanslate); break; case R.id.btn_scale: showAnimation(R.anim.scale, btnScale); break; case R.id.btn_rotate: showAnimation(R.anim.rotate, btnRotate); break; case R.id.btn_alpha: showAnimation(R.anim.alpha, btnAlpha); break; } } }
效果图如下:
除了xml中定义的动画外,还可以通过代码来应用动画
如:
AlphaAnimation alphaAnimation = new AlphaAnimation(0,1); alphaAnimation.setDuration(300); button.setAnimation(alphaAnimation);
其他动画同理
拓展:
通过Animation的setAnimationListener方法可以给view动画添加监听 AlphaAnimation alphaAnimation = new AlphaAnimation(0,1); alphaAnimation.setDuration(300); alphaAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } });
7.1.3、自定义View动画
实际开发中很少自定义view动画,只需要知道是矩阵变换的过程就好
7.1.4、帧动画
帧动画是顺序播放一组预定好的图片
Andorid系统为我们提供了一个类:AnimationDrawable
使用步骤:
1、定义一个xml
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/img1" android:duration="500"/> <item android:drawable="@drawable/img2" android:duration="500"/> <item android:drawable="@drawable/img3" android:duration="500"/> </animation-list>
2、让其作为view的背景即可
button.setBackgroundResource(R.drawable.animation); AnimationDrawable animationDrawable = (AnimationDrawable) button.getBackground(); animationDrawable.start();
帧动画使用比较简单,但是容易引起oom,所以使用帧动画的时候避免使用尺寸较大的图片
7.2、View动画的特殊使用场景
7.2.1、LayoutAnimation
LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,常常被用在listview,recycleview上面,给ViewGroup指定一个动画,那么它的子元素就也有了这个动画
为了给viewGroup子元素加上出场效果,遵循如下几个步骤:
1、定义LayoutAnimation
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" //开始动画的时间延迟 android:delay="0.5" //子元素动画的顺序,normal(顺序),reverse(逆向展示),random(随机展示) android:animationOrder="reverse" //为子元素指定具体的入场动画 android:animation="@anim/anim_item"/>
2、为子元素指定具体的入场
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="300" android:interpolator="@android:anim/accelerate_interpolator" android:shareInterpolator="true" > <alpha android:fromAlpha="0.0" android:toAlpha="1.0" /> <translate android:fromYDelta="500" android:toYDelta="0" /> </set>
3、为ViewGroup指定android:layoutAnimation
<ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" android:layoutAnimation="@anim/anim_layout" android:background="#fff4f7f9" android:cacheColorHint="#00000000" android:divider="#dddbdb" android:dividerHeight="1.0px" android:listSelector="@android:color/transparent" />
除了在XML中定义,我们还可以在代码中定义
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item); LayoutAnimationController controller = new LayoutAnimationController(animation); controller.setDelay(0.5f); controller.setOrder(LayoutAnimationController.ORDER_NORMAL); listview.setLayoutAnimation(controller);
7.2.2、Activity的切换效果
通常我们使用:
Intent in = new Intent(this,DemoActivity_2.class); startActivity(in); overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
在finish的时候:
@Override public void finish() { super.finish(); overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim); }
注意:overridePendingTransition必须在startActivity或者finish的后面
也可以在style.xml中设置
<item name="android:windowAnimationStyle">@style/SmoothWindowAnimation</item> <style name="SmoothWindowAnimation"> <!-- 打开Activity时的入场动画 --> <item name="android:activityOpenEnterAnimation">@anim/slide_right_enter</item> <!-- 打开Activity时的退场动画 --> <item name="android:activityOpenExitAnimation">@anim/slide_left_exit</item> <!-- 关闭Activity时的入场动画 --> <item name="android:activityCloseEnterAnimation">@anim/slide_left_enter</item> <!-- 关闭Activity时的退场动画 --> <item name="android:activityCloseExitAnimation">@anim/slide_right_exit</item> </style>
7.3、属性动画
7.3.1、使用属性动画
属性动画是3.0以后引入的新动画,
常用的动画类有:ValueAnimator、ObjectAnimator、AnimatorSet
Property Animation可以定义在xml文件中
这些xml文件定义的文件路径如下: res/animator/filename.xm
基本用法如下:
<set //together表示集合中的子动画同时播放,sequentially表示子动画顺序播放 android:ordering=["together" | "sequentially"]]]> <objectAnimator //子动画作用对象的名称 android:propertyName="string" //动画的持续时长 android:duration="int" //属性的起始值 android:valueFrom="float | int | color" //属性的结束值 android:valueTo="float | int | color" //动画播放前的延迟时间 android:startOffset="int" //动画的重复次数,默认是0,-1表示无限循环 android:repeatCount="int" //动画的重复模式,repeat连续从头到尾重复, reverse逆向重复,第一次,从头到尾,第二次从尾到头,第三次从头到尾,第四次从尾到头(就像画椭圆一样) android:repeatMode=["repeat" | "reverse"] //表示android:propertyName所指定的类型 android:valueType=["intType" | "floatType"]/> <animator android:duration="int" android:valueFrom="float | int | color" android:valueTo="float | int | color" android:startOffset="int" android:repeatCount="int" android:repeatMode=["repeat" | "reverse"] android:valueType=["intType" | "floatType"]/> <set]]> ... </set> </set>
文件需要有根元素,可以使用, , or . 可以作为一个集合,而且集合中还可以存在元素。
同样也可以通过代码进行设置:
ObjectAnimator的使用:
ObjectAnimator.ofFloat();
ValueAnimator 的使用
ValueAnimator anim = ObjectAnimator.ofInt(); anim.start();
AnimatorSet 的使用
AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(); ObjectAnimator.ofFloat(); ObjectAnimator.ofFloat(); ); set.setDuration(1000).start();
ValueAnimator是Property Animation系统的核心类,它包含了大部分api
objectAnimator继承ValueAnimator,是动画的拓展类
属性动画基本使用如下:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> <objectAnimator android:duration="300" android:propertyName="x" android:valueTo="200" android:valueType="intType" /> <objectAnimator android:duration="300" android:propertyName="Y" android:valueTo="200" android:valueType="intType" /> </set>
使用如下代码调用:
AnimatorSet set = AnimationUtils.loadAnimation(this,R.animator.anim_item) set.setTarget(button); set.start();
在实际开发中我们建议用代码的实现属性动画,因为代码实现比较简单,更加重要的是
很多时候起始值是无法确定的,需要动态的创建动画,更加灵活
7.3.2、理解插值器和估值器
这里我们将了解2个知识点:Interpolate(插值器)和TypeEvaluator(估值器)
常用的插值器如下:
如果系统提供的插值器无法满足我们的需求,我们还可以自定义插值器:
步骤很简单:
步骤一:继承TimeInterpolator
步骤二:重写getInterpolation(float input)方法
代码如下:
public class CustomInterpolator implements TimeInterpolator { @Override public float getInterpolation(float input) { // 编写相关的逻辑计算 //input *= 0.8f; return input * input; } }
7.3.3、属性动画的监听器
Property Animation提供了
Animator.AnimatorListener和Animator.AnimatorUpdateListener两个监听器用于动画在播放过程中的重要动画事件。
下面是两个监听器接口和方法的一些介绍和说明:
Animator.AnimatorListen
onAnimationStart() —— 动画开始时调用;
onAnimationEnd() —— 动画结束时调用;
onAnimationRepeat() —— 动画循环播放时调用;
onAnimationCancel() —— 动画被取消时调用。不管终止的方式如何,被取消的动画仍然会调onAnimationEnd();
Animator.AnimatorUpdateListener:
onAnimationUpdate() —— 动画每播放一帧时调用。在动画过程中,可侦听此事件来获取并使用 ValueAnimator 计算出来的属性值。
利用传入事件的 ValueAnimator 对象,调用其 getAnimatedValue() 方法即可获取当前的属性值。如果使用 ValueAnimator来实现动画的话 ,则必需实现此侦听器。
7.3.4、对任意属性做动画
这里先提出一个问题:
给button加个动画,宽度从当前宽度增加200px,也许你会说用view动画搞定,但是当你做的时候你会发现view动画不支持对宽度进行动画,不过可以使用缩放动画试试:
效果如下:
这样的效果当然不是我们想要的效果,文本都被拉伸了。
下面我们使用属性动画来试试:
private void performAnimate() { ViewWrapper viewWrapper = new ViewWrapper(btnTanslate); ObjectAnimator.ofInt(viewWrapper, "width", 500).setDuration(1000).start(); } private static class ViewWrapper { private View mTarget; public ViewWrapper(View target){ this.mTarget = target; } public int getWidth(){ return mTarget.getLayoutParams().width; } public void setWidth(int width){ mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } }
很显然效果达到了
拓展:
使用ValueAnimator实现动画的步骤及实践 那一般使用ValueAnimator实现动画分为以下七个步骤: 1. 调用ValueAnimation类中的ofInt(int...values)、ofFloat(String propertyName,float...values)等静态方法实例化ValueAnimator对象,并设置目标属性的属性名、初始值或结束值等值; 2.调用addUpdateListener(AnimatorUpdateListener mListener)方法为ValueAnimator对象设置属性变化的监听器; 3.创建自定义的Interpolator,调用setInterpolator(TimeInterpolator value)为ValueAniamtor设置自定义的Interpolator;(可选,不设置默认为缺省值) 4.创建自定义的TypeEvaluator,调用setEvaluator(TypeEvaluator value)为ValueAnimator设置自定义的TypeEvaluator;(可选,不设置默认为缺省值) 5.在AnimatorUpdateListener 中的实现方法为目标对象的属性设置计算好的属性值。 6.设置动画的持续时间、是否重复及重复次数等属性; 7.为ValueAnimator设置目标对象并开始执行动画。
代码如下:
/** * 使用ValueAnimator改变Imageview的margin的值 */ public void marginValueAnimator(){ //1.调用ofInt(int...values)方法创建ValueAnimator对象 ValueAnimator mAnimator = ValueAnimator.ofInt(0,screenWidth - mImageViewTest.getWidth()); //2.为目标对象的属性变化设置监听器 mAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 3.为目标对象的属性设置计算好的属性值 int animatorValue = (int)animation.getAnimatedValue(); MarginLayoutParams marginLayoutParams = (MarginLayoutParams) mImageViewTest.getLayoutParams(); marginLayoutParams.leftMargin = animatorValue; mImageViewTest.setLayoutParams(marginLayoutParams); } }); //4.设置动画的持续时间、是否重复及重复次数等属性 mAnimator.setDuration(2000); mAnimator.setRepeatCount(3); mAnimator.setRepeatMode(ValueAnimator.REVERSE); //5.为ValueAnimator设置目标对象并开始执行动画 mAnimator.setTarget(mImageViewTest); mAnimator.start(); }
关于属性动画更多的介绍请参照
Andorid属性动画完全解析上、中、下
http://blog.csdn.net/guolin_blog/article/details/43536355
http://blog.csdn.net/guolin_blog/article/details/43816093
http://blog.csdn.net/guolin_blog/article/details/44171115
Andorid属性动画完全解析上、下
http://blog.csdn.net/lmj623565791/article/details/38067475
http://blog.csdn.net/lmj623565791/article/details/38092093
其他
http://blog.csdn.net/yegongheng/article/details/38435553
http://blog.csdn.net/xyz_lmn/article/details/38667899
拓展:
看了这么多例子,我不知道那些系统的propertyName一共有哪些,我看了下ObjectAnimator源码才发现,下面贴出来:
static { PROXY_PROPERTIES.put("alpha", PreHoneycombCompat.ALPHA); PROXY_PROPERTIES.put("pivotX", PreHoneycombCompat.PIVOT_X); PROXY_PROPERTIES.put("pivotY", PreHoneycombCompat.PIVOT_Y); PROXY_PROPERTIES.put("translationX", PreHoneycombCompat.TRANSLATION_X); PROXY_PROPERTIES.put("translationY", PreHoneycombCompat.TRANSLATION_Y); PROXY_PROPERTIES.put("rotation", PreHoneycombCompat.ROTATION); PROXY_PROPERTIES.put("rotationX", PreHoneycombCompat.ROTATION_X); PROXY_PROPERTIES.put("rotationY", PreHoneycombCompat.ROTATION_Y); PROXY_PROPERTIES.put("scaleX", PreHoneycombCompat.SCALE_X); PROXY_PROPERTIES.put("scaleY", PreHoneycombCompat.SCALE_Y); PROXY_PROPERTIES.put("scrollX", PreHoneycombCompat.SCROLL_X); PROXY_PROPERTIES.put("scrollY", PreHoneycombCompat.SCROLL_Y); PROXY_PROPERTIES.put("x", PreHoneycombCompat.X); PROXY_PROPERTIES.put("y", PreHoneycombCompat.Y); }
有了这些,你传入对应的propertyName,就可以实现各种各样的功能啦
package com.nineoldandroids.animation;里面找到的,
package android.animation;包里面没有。
7.3.5、属性动画的工作原理
属性动画要求动画作用的对象提供该属性的set方法,属性动画根据你提供的开始值和最终值,多次调用set方法,每次传的值都不一样。
下面我们看源码分析:
1、我们通常使用都是ObjectAnimator.ofInt().start();
那我们就从start()方法看看,
@Override public void start() { // See if any of the current active/pending animators need to be canceled AnimationHandler handler = sAnimationHandler.get(); if (handler != null) { int numAnims = handler.mAnimations.size(); //判断当前动画是否重复 for (int i = numAnims - 1; i >= 0; i--) { if (handler.mAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } numAnims = handler.mPendingAnimations.size(); //判断等待动画是否重复 for (int i = numAnims - 1; i >= 0; i--) { if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } numAnims = handler.mDelayedAnims.size(); //判断延迟动画是否重复 for (int i = numAnims - 1; i >= 0; i--) { if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) { ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i); if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) { anim.cancel(); } } } } if (DBG) { Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvh = mValues[i]; Log.d(LOG_TAG, " Values[" + i + "]: " + pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " + pvh.mKeyframes.getValue(1)); } } super.start(); }
代码看似很多,其实做的事情很简单
首先判断
如果当前动画(mAnimations)、等待的动画(mPendingAnimations)、延迟动画(mDelayedAnims)它们之中只要有和当前动画相同的动画(通过instanceof 进行比较),那么就调用 anim.cancel();来取消当前的动画;
接下来是一段LOG日志
最后如果都不重复的话就直接调用父类的启动动画方法了(super.start());
因为ObjectAnimator继承ValueAnimator那么我们再来看看ValueAnimator的start()方法
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mReversing = playBackwards; mPlayingBackwards = playBackwards; if (playBackwards && mSeekFraction != -1) { if (mSeekFraction == 0 && mCurrentIteration == 0) { // special case: reversing from seek-to-0 should act as if not seeked at all mSeekFraction = 0; } else if (mRepeatCount == INFINITE) { mSeekFraction = 1 - (mSeekFraction % 1); } else { mSeekFraction = 1 + mRepeatCount - (mCurrentIteration + mSeekFraction); } mCurrentIteration = (int) mSeekFraction; mSeekFraction = mSeekFraction % 1; } if (mCurrentIteration > 0 && mRepeatMode == REVERSE && (mCurrentIteration < (mRepeatCount + 1) || mRepeatCount == INFINITE)) { // if we were seeked to some other iteration in a reversing animator, // figure out the correct direction to start playing based on the iteration if (playBackwards) { mPlayingBackwards = (mCurrentIteration % 2) == 0; } else { mPlayingBackwards = (mCurrentIteration % 2) != 0; } } int prevPlayingState = mPlayingState; mPlayingState = STOPPED; mStarted = true; mStartedDelay = false; mPaused = false; updateScaledDuration(); // in case the scale factor has changed since creation time AnimationHandler animationHandler = getOrCreateAnimationHandler(); animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running if (prevPlayingState != SEEKED) { setCurrentPlayTime(0); } mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); } animationHandler.start(); }
通过上述代码,我们可以看出,属性动画需要运行在looper的线程中,最终会调用 animationHandler.start();
然后调用到JNI层,最后回调回来。
这边有一个核心方法:doAnimationFrame
其中animationFrame内部调用animateValue
在初始化的时候如果属性没有提供初始值,则get方法将被会调用,我们看PropertyValuesHolder的setValue方法
private void setupValue(Object target, Keyframe kf) { if (mProperty != null) { Object value = convertBack(mProperty.get(target)); kf.setValue(value); } try { if (mGetter == null) { Class targetClass = target.getClass(); setupGetter(targetClass); if (mGetter == null) { // Already logged the error - just return to avoid NPE return; } } Object value = convertBack(mGetter.invoke(target)); kf.setValue(value); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } }
可以看到get方法是通过反射调用的,当动画到下一帧的时候,调用setAnimatedValue将新的属性设置给对象,调用set方法。
set方法也是通过反射调用的
代码如下:
void setAnimatedValue(Object target) { if (mProperty != null) { mProperty.set(target, getAnimatedValue()); } if (mSetter != null) { try { mTmpValueArray[0] = getAnimatedValue(); mSetter.invoke(target, mTmpValueArray); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } } }
相关文章推荐
- Android开发艺术探索——第七章:Android动画深入分析
- Android 开发艺术探索笔记 第七章 Android动画深入分析
- Android开发艺术-第七章Android动画深入分析读书笔记
- 【读书笔记】【Android 开发艺术探索】第 7 章 Android 动画深入分析
- Android开发艺术探索__android动画深入分析(七)
- 开发艺术探索 -- 动画深入分析
- 第六章Andorid的Drawable(Android开发艺术探索)
- android开发艺术探索 学习笔记(一) Activity生命周期全面分析
- Andorid开发艺术探索读书笔记--Android四大组件
- 第七章、Android动画深入分析
- Android开发艺术探索 第六章 第七章
- android开发艺术探索 2 Activity异常情况下的生命周期分析
- 第七章 Android动画深入分析
- android 艺术开发探索之动画(animation)
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画深入分析
- android开发之Service深入分析全解
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画深入分析:让你成为动画牛人