转载了各路大神,为了copy啊啊啊,后面继续补充完善:
Android 三种动画详解 https://www.cnblogs.com/ldq2016/p/5407061.htmlAndroid动画学习(三)之使用ValueAnimator和ObjectAnimator实现动画实例
https://blog.csdn.net/yegongheng/article/details/38435553
一、. View Animation 视图动画(补间动画)
1.8 视图动画使用方二、 Drawable Animation(帧动画 , AnimationDrawable extends DrawableContainer )使用详解
三、 Property Animation(属性动画)使用详解
3.1属性动画概述
3.2 属性动画计算原理
3.3 XML方式属性动画
3.4 Java方式属性动画
3.5 ValueAnimator有两个子类,一个是TimeAnimator,一个是ObjectAnimator
3.5.1 TimeAnimator使用
3.5.2 ObjectAnimator使用
(1) 特别注意(ObjectAnimator实现动画也有一些要求和限制,一般有以下几点需要注意):
属性动画示例
3.6 PropertyValuesHolder使用
3.7 AnimationSet的使用方法
3.8 Evaluators相关类解释
3.9 Interpolators相关类解释
3.10 Java属性动画拓展之ViewPropertyAnimator动画
(1) View 类中新增的便于实现 property 动画的属性( ** ):
3.11 Java属性动画拓展之LayoutAnimator容器布局动画
LayoutAnimator示例
android 动画现在分为大致3类 + 插值器
View Animation:
视图动画在古老的Android版本系统中就已经提供了,只能被用来设置View的动画。
Drawable Animation:
这种动画(也叫Frame动画、帧动画)其实可以划分到视图动画的类别,专门用来一个一个的显示Drawable的resources,就像放幻灯片一样。
Property Animation:
属性动画只对Android 3.0(API 11)以上版本的Android系统才有效,这种动画可以设置给任何Object,包括那些还没有渲染到屏幕上的对象。
这种动画是可扩展的,可以让你自定义任何类型和属性的动画。
视图动画 Interpolator 插值器详解( 后面单独开篇章)
TimeInterpolator 时间插值器
TypeEvaluator 类型估值算法
java类 xml id值 描述
AccelerateDecelerateInterpolator(加速减速插值器,两头慢中间快) @android:anim/accelerate_decelerate_interpolator 动画始末速率较慢,中间加速
AccelerateInterpolator @android:anim/accelerate_interpolator 动画开始速率较慢,之后慢慢加速
AnticipateInterpolator @android:anim/anticipate_interpolator 开始的时候从后向前甩
AnticipateOvershootInterpolator @android:anim/anticipate_overshoot_interpolator 类似上面AnticipateInterpolator
BounceInterpolator @android:anim/bounce_interpolator 动画结束时弹起
CycleInterpolator @android:anim/cycle_interpolator 循环播放速率改变为正弦曲线
DecelerateInterpolator @android:anim/decelerate_interpolator 动画开始快然后慢
LinearInterpolator @android:anim/linear_interpolator 动画匀速改变
OvershootInterpolator @android:anim/overshoot_interpolator 向前弹出一定值之后回到原来位置
PathInterpolator 新增,定义路径坐标后按照路径坐标来跑。
如上就是系统提供的一些插值器,下面我们来看看怎么使用他们。
插值器使用方法
插值器的使用比较简答,如下和:
<set android:interpolator="@android:anim/accelerate_interpolator">
...
</set>
插值器的自定义:
有时候你会发现系统提供的插值器不够用,可能就像View一样需要自定义。所以接下来我们来看看插值器的自定义,关于插值器的自定义分为两种实现方式:
<1 xml自定义实现(其实就是对现有的插值器的一些属性修改)
在res/anim/目录下创建filename.xml文件,修改你准备自定义的插值器如下:
<?xml version="1.0" encoding="utf-8"?>
<InterpolatorName xmlns:android="http://schemas.android.com/apk/res/android"
android:attribute_name="value"
/>
在你的补间动画文件中引用该文件即可。
可以看见上面第二步修改的是现有插值器的一些属性,但是有些插值器却不具备修改属性,具体如下:
<accelerateDecelerateInterpolator>
无可自定义的attribute。
<accelerateInterpolator>
android:factor 浮点值,加速速率(默认值为1)。
<anticipateInterploator>
android:tension 浮点值,起始点后拉的张力数(默认值为2)。
<anticipateOvershootInterpolator>
android:tension 浮点值,起始点后拉的张力数(默认值为2)。
android:extraTension 浮点值,拉力的倍数(默认值为1.5)。
<bounceInterpolator>
无可自定义的attribute。
<cycleInterplolator>
android:cycles 整形,循环的个数(默认为1)。
<decelerateInterpolator>
android:factor 浮点值,减速的速率(默认为1)。
<linearInterpolator>
无可自定义的attribute。
<overshootInterpolator>
android:tension 浮点值,超出终点后的张力(默认为2)。
<2 java代码实现方式
再来看看Java自定义插值器的(Java自定义插值器其实是xml自定义的升级,也就是说如果我们修改xml的属性还不能满足需求,那就可以选择通过Java来实现)方式。
可以看见上面所有的Interpolator都实现了Interpolator接口,而Interpolator接口又继承自 TimeInterpolator,
TimeInterpolator接口定义了一个float getInterpolation(float input);方法,这个方法是由系统调用的,
其中的参数input代表动画的时间,在0和1之间,也就是开始和结束之间。
如下就是一个动画始末速率较慢、中间加速的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;
}
......
}
(插值因子)计算
我们可以使用以上系统提供的插值器对目标对象实现一些诸如先加速后加速、弹跳和回移的动画效果。
那如果以上这些插值器都无法满足我们的具体开发需求的话,那么也可以通过继承TimeInterpolator类来自定义自己的Interpolator,
在自定义的Interpolator类中只需要要重写getInterpolation(float input)方法,
并在该方法通过相关的计算,再返回计算后的结果(插值因子)就搞定,具体示例如下:
public class CustomInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
// 编写相关的逻辑计算
//input *= 0.8f;
return input * input;
}
}
自定义TypeEvaluator
一View Animation 视图动画(补间动画)使用详解 ***********************************************************************
视图动画,也叫Tween(补间)动画可以在一个视图容器内执行一系列简单变换(位置、大小、旋转、透明度)。
譬如,如果你有一个TextView对象,您可以移动、旋转、缩放、透明度设置其文本,当然,如果它有一个背景图像,背景图像会随着文本变化。
补间动画通过XML或Android代码定义,建议使用XML文件定义,因为它更具可读性、可重用性。
1.1如下是视图动画相关的类继承关系:
java类名 xml关键字 描述信息
AlphaAnimation <alpha> 放置在res/anim/目录下 渐变透明度动画效果
RotateAnimation <rotate> 放置在res/anim/目录下 画面转移旋转动画效果
ScaleAnimation <scale> 放置在res/anim/目录下 渐变尺寸伸缩动画效果
TranslateAnimation <translate> 放置在res/anim/目录下 画面转换位置移动动画效果
AnimationSet <set> 放置在res/anim/目录下 一个持有其它动画元素alpha、scale、translate、rotate或者其它set元素的容器
1.2 Animation 通用属性详解
xml属性 java方法 解释
android:detachWallpaper setDetachWallpaper(boolean) 是否在壁纸上运行
android:duration setDuration(long) 动画持续时间,毫秒为单位
android:fillAfter setFillAfter(boolean) 控件动画结束时是否保持动画最后的状态
android:fillBefore setFillBefore(boolean) 控件动画结束时是否还原到开始动画前的状态
android:fillEnabled setFillEnabled(boolean) 与android:fillBefore效果相同
android:interpolator setInterpolator(Interpolator) 设定插值器(指定的动画效果,譬如回弹等)
android:repeatCount setRepeatCount(int) 重复次数
android:repeatMode setRepeatMode(int) 重复类型有两个值,reverse表示倒序回放,restart表示从头播放
android:startOffset setStartOffset(long) 调用start函数之后等待开始运行的时间,单位为毫秒
android:zAdjustment setZAdjustment(int) 表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal
1.3 Alpha属性详解
xml属性 java方法 解释
android:fromAlpha AlphaAnimation(float fromAlpha, …) 动画开始的透明度(0.0到1.0,0.0是全透明,1.0是不透明)
android:toAlpha AlphaAnimation(…, float toAlpha) 动画结束的透明度,同上
1.4 Rotate属性详解
xml属性 java方法 解释
android:fromDegrees RotateAnimation(float fromDegrees, …) 旋转开始角度,正代表顺时针度数,负代表逆时针度数
android:toDegrees RotateAnimation(…, float toDegrees, …) 旋转结束角度,正代表顺时针度数,负代表逆时针度数
android:pivotX RotateAnimation(…, float pivotX, …) 缩放起点X坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:pivotY RotateAnimation(…, float pivotY) 缩放起点Y坐标,同上规律
1.5 Scale属性详解
xml属性 java方法 解释
android:fromXScale ScaleAnimation(float fromX, …) 初始X轴缩放比例,1.0表示无变化
android:toXScale ScaleAnimation(…, float toX, …) 结束X轴缩放比例
android:fromYScale ScaleAnimation(…, float fromY, …) 初始Y轴缩放比例
android:toYScale ScaleAnimation(…, float toY, …) 结束Y轴缩放比例
android:pivotX ScaleAnimation(…, float pivotX, …) 缩放起点X轴坐标
(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、
50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:pivotY ScaleAnimation(…, float pivotY) 缩放起点Y轴坐标,同上规律
1.6 Translate属性详解
xml属性 java方法 解释
android:fromXDelta TranslateAnimation(float fromXDelta, …) 起始点X轴坐标
(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、
50%p表示以当前View的左上角加上父控件宽高的50%做为初始点)
android:fromYDelta TranslateAnimation(…, float fromYDelta, …) 起始点Y轴从标,同上规律
android:toXDelta TranslateAnimation(…, float toXDelta, …) 结束点X轴坐标,同上规律
android:toYDelta TranslateAnimation(…, float toYDelta) 结束点Y轴坐标,同上规律
1.7 AnimationSet
AnimationSet继承自Animation,是上面四种的组合容器管理类,没有自己特有的属性,他的属性继承自Animation,
所以特别注意:当我们对set标签使用Animation的属性时会对该标签下的所有子控件都产生影响。
1.8 视图动画使用方法 (xml方法, java 方式)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@[package:]anim/interpolator_resource"
android:shareInterpolator=["true" | "false"] >
<alpha
android:fromAlpha="float"
android:toAlpha="float" />
<scale
android:fromXScale="float"
android:toXScale="float"
android:fromYScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float" />
<translate
android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float" />
<rotate
android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float" />
<set>
...
</set>
</set>
ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);
AnimationSet set = new AnimationSet(false);
Animation animation = new AlphaAnimation(0,1); //AlphaAnimation 控制渐变透明的动画效果
animation.setDuration(500); //动画时间毫秒数
set.addAnimation(animation); //加入动画集合
animation = new TranslateAnimation(1, 13, 10, 50); //ScaleAnimation 控制尺寸伸缩的动画效果
animation.setDuration(300);
set.addAnimation(animation);
animation = new RotateAnimation(30,10); //TranslateAnimation 控制画面平移的动画效果
animation.setDuration(300);
//animation = new RotateAnimation(180, 10);
//animation.setDuration(1000);
set.addAnimation(animation);
animation = new ScaleAnimation(5,0,2,0); //RotateAnimation 控制画面角度变化的动画效果
animation.setDuration(300);
set.addAnimation(animation);
LayoutAnimationController controller = new LayoutAnimationController(set, 1);
GridView gridView = (GridView) this.findViewById(R.id.gridview);
gridView .setLayoutAnimation(controller); //GridView 设置动画效果
ListView listview= (ListView)this.findViewById(R.id.listview);
listview.setLayoutAnimation(controller); //ListView 设置动画效果
1.9 Animation类的方法 较实用的方法介绍解释
reset() 重置Animation的初始化
cancel() 取消Animation动画
start() 开始Animation动画
setAnimationListener(AnimationListener listener) 给当前Animation设置动画监听
hasStarted() 判断当前Animation是否开始
hasEnded() 判断当前Animation是否结束
既然补间动画只能给View使用,那就来看看View中和动画相关的几个常用方法吧,如下:
View类的常用动画操作方法 解释
startAnimation(Animation animation) 对当前View开始设置的Animation动画
clearAnimation() 取消当View在执行的Animation动画
到此整个Android的补间动画常用详细属性及方法全部介绍完毕,如有特殊的属性需求可以访问Android Developer查阅即可。
如下我们就来个综合大演练。
PS: 视图动画注意事项
特别特别注意:补间动画执行之后并未改变View的真实布局属性值。切记这一点,譬如我们在Activity中有一个 Button在屏幕上方,
我们设置了平移动画移动到屏幕下方然后保持动画最后执行状态呆在屏幕下方,这时如果点击屏幕下方动画执行之后的Button是没 有任何反应的,
而点击原来屏幕上方没有Button的地方却响应的是点击Button的事件。
二、 Drawable Animation(帧动画 , AnimationDrawable extends DrawableContainer )使用详解 *****************
Drawable动画其实就是Frame动画(帧动画),它允许你实现像播放幻灯片一样的效果,
这种动画的实质其实是Drawable,所以这种动画的XML定义方式文件一般放在res/drawable/目录下。
Drawable动画详细说明
我们依旧可以使用xml或者java方式实现帧动画。但是依旧推荐使用xml,具体如下:
<animation-list> 必须是根节点,包含一个或者多个<item>元素,属性有:
android:oneshot true代表只执行一次,false循环执行。
<item> 类似一帧的动画资源。
<item> animation-list的子项,包含属性如下:
android:drawable 一个frame的Drawable资源。
android:duration 一个frame显示多长时间。
3-3 Drawable动画实例演示
关于帧动画相对来说比较简单,这里给出一个常规使用框架,如下:
<!-- 注意:rocket.xml文件位于res/drawable/目录下 -->
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot=["true" | "false"] >
<item
android:drawable="@[package:]drawable/drawable_resource_name"
android:duration="integer" />
</animation-list>
ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.rocket_thrust);
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
rocketAnimation.start();
rocketAnimation.stop();
java 代码 ----------
private AnimationDrawable frameAnim;
frameAnim =new AnimationDrawable();
// 为AnimationDrawable添加动画帧
frameAnim.addFrame(getResources().getDrawable(R.drawable.img0), 50);
frameAnim.addFrame(getResources().getDrawable(R.drawable.img1), 50);
frameAnim.addFrame(getResources().getDrawable(R.drawable.img2), 50);
frameAnim.addFrame(getResources().getDrawable(R.drawable.img3), 50);
frameAnim.addFrame(getResources().getDrawable(R.drawable.img4), 50);
frameAnim.addFrame(getResources().getDrawable(R.drawable.img5), 50);
frameAnim.addFrame(getResources().getDrawable(R.drawable.img6), 50);
frameAnim.addFrame(getResources().getDrawable(R.drawable.img7), 50);
// 设置为循环播放
frameAnim.setOneShot(false);
// 设置ImageView的背景为AnimationDrawable
iv_frame.setBackgroundDrawable(frameAnim);
特别注意,AnimationDrawable的start()方法不能在Activity的onCreate方法中调运,
因为AnimationDrawable还未完全附着到window上,所以最好的调运时机是onWindowFocusChanged()方法中。
三、 Property Animation(属性动画)使用详解 **********************************************************
概述:
在使用属性动画之前先来看几个常用的View属性成员:
translationX,translationY:控制View的位置,值是相对于View容器左上角坐标的偏移。
rotationX,rotationY: 控制相对于轴心旋转。
x,y: 控制View在容器中的位置,即左上角坐标加上translationX和translationY的值。
alpha: 控制View对象的alpha透明度值。
Property Animation提供了监听器用于动画在播放过程中的重要动画事件,监听器接口和方法的一些介绍和说明:
Animator.AnimatorListener:
onAnimationStart() —— 动画开始时调用;
onAnimationEnd() —— 动画结束时调用;
onAnimationRepeat() —— 动画循环播放时调用;
onAnimationCancel() —— 动画被取消时调用。不管终止的方式如何,被取消的动画仍然会调onAnimationEnd();
Animator.AnimatorPauseListener
onAnimationPause(Animator animation);
onAnimationResume(Animator animation);
ValueAnimator.AnimatorUpdateListener :
onAnimationUpdate() —— 动画每播放一帧时调用。在动画过程中,可侦听此事件来获取并使用 ValueAnimator 计算出来的属性值。
利用传入事件的 ValueAnimator 对象,调用其 getAnimatedValue() 方法即可获取当前的属性值。
如果使用 ValueAnimator来实现动画的话 ,则必需实现此侦听器。
使用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设置目标对象并开始执行动画。
PropertyValuesHolder 多属性动画同时工作管理类。
3.1属性动画概述
Android 3.0以后引入了属性动画,属性动画可以轻而易举的实现许多View动画做不到的事
View动画无非也就做那几种事情,别的也搞不定,而属性动画就可以的,譬如3D旋转一张图片。
属性动画实现原理就是修改控件的属性值实现的动画。
具体先看下类关系:
/**
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
public abstract class Animator implements Cloneable {
......
}
java类名 xml关键字 描述信息
ValueAnimator <animator> 放置在res/animator/目录下 在一个特定的时间里执行一个动画
TimeAnimator 时序监听回调工具
ObjectAnimator <objectAnimator> 放置在res/animator/目录下 一个对象的一个属性动画
AnimatorSet <set> 放置在res/animator/目录下 动画集合
我们平时使用属性动画的重点就在于AnimatorSet、ObjectAnimator、TimeAnimator、ValueAnimator。
3.2 属性动画计算原理
(x=0,t=0ms) -> (x=10,t=10ms)-> (x=20,t=20ms)-> (x=30,t=30ms)-> (x=40,t=40ms)
上面就是一个线性匀速动画,描述了一个Object的X属性运动动画,该对象的X坐标在40ms内从0移动到40,
每10ms刷新一次,移动4次,每次移动为40/4=10pixel。
(x=0,t=0ms) -> (x=6,t=10ms) --加速-> (x=20,t=20ms) --加速--> (x=34,t=30ms) --> (x=40,t=40ms)
上面是一个非匀速动画,描述了一个Object的X属性运动动画,该对象的X坐标在40ms内从0移动到40,
每10ms刷新一次,移动4次,但是速率不同,开始和结束的速度要比中间部分慢,即先加速后减速。
接下来我们来详细的看一下,属性动画系统的重要组成部分是如何计算动画值的,下图描述了如上面所示动画的实现作用过程。
其中的ValueAnimator是动画的执行类,跟踪了当前动画的执行时间和当前时间下的属性值;
ValueAnimator封装了动画的 TimeInterpolator时间插值器和一个TypeEvaluator类型估值,用于设置动画属性的值,就像上面图2非线性动画 里,
TimeInterpolator使用了AccelerateDecelerateInterpolator、TypeEvaluator使用了 IntEvaluator。
为了执行一个动画,你需要创建一个ValueAnimator,并且指定目标对象属性的开始、结束值和持续时间。在调用start后,整个动画过程 中,
ValueAnimator会根据已经完成的动画时间计算得到一个0到1之间的分数,代表该动画的已完成动画百分比。
0表示0%,1表示100%,譬如上 面图一线性匀速动画中总时间 t = 40 ms,t = 10 ms的时候是 0.25。
当ValueAnimator计算完已完成动画分数后,它会调用当前设置的TimeInterpolator,去计算得到一个 interpolated(插值)分数,在计算过程中,
已完成动画百分比会被加入到新的插值计算中。如上图2非线性动画中,因为动画的运动是缓慢加速的, 它的插值分数大约是 0.15,小于t = 10ms时的已完成动画分数0.25。
而在上图1中,这个插值分数一直和已完成动画分数是相同的。
当插值分数计算完成后,ValueAnimator会根据插值分数调用合适的 TypeEvaluator去计算运动中的属性值。
好了,现在我们来看下代码就明白这段话了,上面图2非线性动画里,TimeInterpolator使用了 AccelerateDecelerateInterpolator、TypeEvaluator使用了IntEvaluator。
所以这些类都是标准的 API,我们来看下标准API就能类比自己写了,如下:
首先计算已完成动画时间分数(以10ms为例):t=10ms/40ms=0.25。
接着看如下源码如何实现计算差值分数的:
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}
......
//这是我们关注重点,可以发现如下计算公式计算后(input即为时间因子)插值大约为0.15。
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
......
}
其实AccelerateDecelerateInterpolator的基类接口就是TimeInterpolator,如下,他只有getInterpolation方法,也就是上面我们关注的方法。
public interface TimeInterpolator {
float getInterpolation(float input);
}
接着ValueAnimator会根据插值分数调用合适的TypeEvaluator(IntEvaluator)去计算运动中的属性值,如下,
因为startValue = 0,所以属性值:0+0.15*(40-0)= 6。
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
这就是官方给的一个关于属性动画实现的过程及基本原理解释
3.3 XML方式属性动画
在xml中可直接用的属性动画节点有ValueAnimator、ObjectAnimator、AnimatorSet。
<set
android:ordering=["together" | "sequentially"]>
<objectAnimator
android:propertyName="string"
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"]/>
<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>
<set>属性解释:
xml属性 解释
android:ordering 控制子动画启动方式是先后有序的还是同时进行。sequentially:动画按照先后顺序;together(默认):动画同时启动;
<objectAnimator>属性解释:
xml属性 解释
android:propertyName String类型,必须要设置的节点属性,代表要执行动画的属性(通过名字引用),辟如你可以指定了一个View的”alpha” 或者 “backgroundColor” ,这个objectAnimator元素没有对外说明target属性,所以你不能在XML中设置执行这个动画,必须通过调用 loadAnimator()方法加载你的XML动画资源,然后调用setTarget()应用到具备这个属性的目标对象上(譬如TextView)。
android:valueTo float、int或者color类型,必须要设置的节点属性,表明动画结束的点;如果是颜色的话,由6位十六进制的数字表示。
android:valueFrom 相对应valueTo,动画的起始点,如果没有指定,系统会通过属性的get方法获取,颜色也是6位十六进制的数字表示。
android:duration 动画的时长,int类型,以毫秒为单位,默认为300毫秒。
android:startOffset 动画延迟的时间,从调用start方法后开始计算,int型,毫秒为单位。
android:repeatCount 一个动画的重复次数,int型,”-1“表示无限循环,”1“表示动画在第一次执行完成后重复执行一次,也就是两次,默认为0,不重复执行。
android:repeatMode 重复模式:int型,当一个动画执行完的时候应该如何处理。该值必须是正数或者是-1,“reverse”会使得按照动画向相反的方向执行,可实现类似钟摆效果。“repeat”会使得动画每次都从头开始循环。
android:valueType 关键参数,如果该value是一个颜色,那么就不需要指定,因为动画框架会自动的处理颜色值。
有intType和floatType(默认)两种:分别说明动画值为int和float型。
intType 指定动画的值是整数
floatType(默认) 指定动画的值是浮点数
<objectAnimator>属性解释:
同上<objectAnimator>属性,不多介绍。
XML属性动画使用方法:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.animtor.property_animator);
set.setTarget(myObject);
set.start();
3.4 Java方式属性动画
ValueAnimator
特别注意:ValueAnimator只是动画计算管理驱动,设置了作用目标,但没有设置属性,需要通过updateListener里设置属性才会生效。
属性动画中的时间驱动,管理着动画时间的开始、结束属性值,相应时间属性值计算方法等。
包含所有计算动画值的核心函数以及每一个动画时间节点上的信息、一个动画是否重复、是否监听更新事件等,并且还可以设置自定义的计算类型。
大眼看上去可以发现和ObjectAnimator没啥区别,实际上正是由于ValueAnimator不直接操作属性值,
所以要操作对象的属性可以不需要setXXX与getXXX方法,你完全可以通过当前动画的计算去修改任何属性。
例1:
if (valueAnimator.isStarted()) {
valueAnimator.cancel();
} else {
valueAnimator.start();
}
ValueAnimator animator = ValueAnimator.ofFloat(0, mContentHeight); //定义动画
animator.setTarget(view); //设置作用目标
animator.setDuration(5000).start();
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation){
float value = (float) animation.getAnimatedValue();
view.setXXX(value); //必须通过这里设置属性值才有效
view.mXXX = value; //不需要setXXX属性方法
}
});
例2:
ValueAnimator anim = ValueAnimator.ofInt(0, 100);
anim.setDuration(100);
anim.start();
/*然后通过ValueAnimator的AnimatorUpdateListener可以得到动画执行每一帧所返回的值,
利用返回的值对View处理动画效果*/
anim.setEvaluator(new TypeEvaluator<Integer>() {
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
return (int)((startValue + fraction * (endValue - startValue)) / 10 * 10);
}
});
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//frameValue是通过startValue和endValue以及Fraction计算出来的
int frameValue = (Integer)animation.getAnimatedValue();
//利用每一帧返回的值,可以做一些改变View大小,坐标,透明度等等操作
//用处可以举一反三 (TimeAnimator更好用)
}
});
例3: 多个属性动画同时执行的方法
ValueAnimator animator = ValueAnimator.ofFloat(0f,200f);
animator.setDuration(200);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//动画在执行的过程当中,不断地调用此方法
animation.getAnimatedFraction()//百分比
//得到duration时间内 values当中的某一个中间值。0f~100f
float value = (float) animation.getAnimatedValue();//
iv.setScaleX(0.5f+value/200);//0.5~1
iv.setScaleY(0.5f+value/200);//0.5~1
}
});
animator.start();
//3)方法3
//float... values:代表关键帧的值
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("alpha", 1f,0.7f,1f);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleX", 1f,0.7f,1f);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("scaleY", 1f,0.7f,1f);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("translationX", 0f,300f);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(iv, holder1,holder2,holder3);
animator.setDuration(1000);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
float animatedValue = (float) animation.getAnimatedValue();
float animatedFraction = animation.getAnimatedFraction();
long playTime = animation.getCurrentPlayTime();
System.out.println("animatedValue:"+animatedValue+", playTime:"+playTime);
}
});
animator.start();
//4)方法4:-----------------动画集合--------------------
ObjectAnimator animator1 = ObjectAnimator.ofFloat(iv,"alpha", 1f,0.7f,1f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(iv,"scaleX", 1f,0.7f,1f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(iv,"scaleY", 1f,0.7f,1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(500);
//animatorSet.play(anim);//执行当个动画
animatorSet.playTogether(animator1,animator2,animator3);//同时执行
animatorSet.playSequentially(animator1,animator2,animator3);//依次执行动画
animatorSet.start();
例4:ObjectAnimator动画实现贝塞尔曲线样式
ObjectAnimator animatorX,animatorY;
animatorX = ObjectAnimator.ofFloat(same_level, "X",0,600).setDuration(1000);
animatorY = ObjectAnimator.ofFloat(same_level, "Y",800,200).setDuration(1000);
animatorY.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
MyToast.showToast(SameCaseActy.this,im.getScaleX()+"");
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
same_level.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
animatorY.setInterpolator(new LinearInterpolator());
animatorX.setInterpolator(new PathInterpolator(0.4f,0,0.2f,1));
animatorX.start();
animatorY.start();
}
});
3.5 ValueAnimator有两个子类,一个是TimeAnimator,一个是ObjectAnimator
3.5.1 TimeAnimator使用
在api 16才引入,它并不能直接实现动画效果,而是在TimeListener里返回动画持续的时间,
与上次调用的间隔时间,要怎么改变view,需要自己操作。
TimeAnimator:提供了一个简单的回调机制:
通过 TimeAnimator.TimeListener,在动画的每一帧处通知你。这个动画器没有时间,插值或是对象值设定。
回调监听器为每一帧动画接受信息,包括总运行时间和从前一帧到现在的运行时间.
相关方法:
setTimeListener(TimeAnimator.TimeListener listener):设置监听器.
start():开始动画
TimeAnimator.TimeListener:
实现这个接口可以对动画的每一帧进行监听
onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime)
animation:发出通知的动画
totalTime:动画开始以来的总时间,以毫秒为单位
deltaTime:从前一帧到现在的运行时间,以毫秒为单位
ObjectAnimator objectAnimator=ObjectAnimator.ofInt(progressBar,"progress",1,100);
objectAnimator.setDuration(10000);
objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
objectAnimator.start();
//api 16 android 4.1以上版本才有
TimeAnimator timeAnimator=new TimeAnimator();
timeAnimator.setTimeListener(new TimeAnimator.TimeListener() {
@Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
System.out.println(totalTime+" "+deltaTime);}
});
timeAnimator.start();
3.5.2 ObjectAnimator使用
继承自ValueAnimator,允许你指定要进行动画的对象以及该对象 的一个属性。该类会根据计算得到的新值自动更新属性。
大多数的情况使用ObjectAnimator就足够了,因为它使得目标对象动画值的处理过程变得足 够简单,
不用像ValueAnimator那样自己写动画更新的逻辑,但是ObjectAnimator有一定的限制,比如它需要目标对象的属性提供指定的处理方法(譬如提供getXXX,setXXX方法),
这时候你就需要根据自己的需求在ObjectAnimator和ValueAnimator中看 哪种实现更方便了。
ObjectAnimator类提供了
ofInt、
ofFloat、
ofObject
这个三个常用的方法,
这些方法都是设置动画作用的元素、属性、开始、结束等任意属性值。当属性值(上面方法的参数)只设置一个时就把通过getXXX反射获取的值作为起点,
设置的值作为终点;如果设置两个(参数),那么一个是开始、另一个是结束。
(1) 特别注意(ObjectAnimator实现动画也有一些要求和限制,一般有以下几点需要注意):
ObjectAnimator的动画原理是不停的调用setXXX方法更新属性值,所有使用ObjectAnimator更新属性时的前提是:Object必须声明有getXXX和setXXX方法。
因为 ObjectAnimator 会在动画期间自动更新属性值,它必须能够用此 setter 方法访问到该属性。 例如:假设属性名称为foo,则需要有一个setFoo()方法。 而你如果此 setter 方法不存在,那么我们可以有以下三种选择:
<1>如果权限允许的话,直接在类中增加此 setter 方法;
<2>修改封装类来增加此 setter 方法,并让该封装类来接收属性值并传给初始的对象;
<3>换用 ValueAnimator。
2.如果在调用 ObjectAnimator 的某个工厂方法时,我们只为 values... 参数指定了一个值,那此值将被认定为动画属性的结束值。
这样的话,动画显示的属性必须带有一个 getter 方法,用于获取动画的起始值。 此 getter 方法必须以get<propertyName>()的格式命名。
例如:假设属性名为foo,则需要有一个getFoo()方法。
3.动画属性的 getter 方法(如果必要的话)和 setter 方法所操作数据的类型必须与 ObjectAnimator 中设定的起始和结束值相同。
我们通常使用ObjectAnimator设置View已知的属性来生成动画,而一般View已知属性变化时都会主动触发重绘图操作,所以动画会自 动实现;
但是也有特殊情况,譬如作用Object不是View,或者作用的属性没有触发重绘,或者我们在重绘时需要做自己的操作,那都可以通过如下方法手 动设置!
属性动画示例:
例0:简单y轴下移
mAnimator = ObjectAnimator.ofFloat(tv, "translationY", 0, 900);
mAnimator.setDuration(2000);
mAnimator.setStartDelay(2000);
例1:简单设置
<1
ObjectAnimator mObjectAnimator= ObjectAnimator.ofInt(view, "customerDefineAnyThingName", 0, 1).setDuration(2000);
mObjectAnimator.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
//int value = animation.getAnimatedValue(); 可以获取当前属性值
//view.postInvalidate(); 可以主动刷新
//view.setXXX(value);
//view.setXXX(value);
//......可以批量修改属性
}
});
<2 向右移动500pix,然后再移动到原来的位置复原。
float translationX = text.getTranslationX();
// 参数“translationX”指明在x坐标轴位移,即水平位移。
ObjectAnimator anim = ObjectAnimator.ofFloat(text, "translationX", translationX, -500f, translationX);
anim.setDuration(5000);
// 回调监听,可以有也可以无。
// 根据情况,如果需要监听动画执行到何种“进度”,那么就监听之。
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
Log.d("zhangphil", value + "");
}
});
// 正式开始启动执行动画
anim.start();
<3 如下是一个我在项目中的Y轴3D旋转动画实现实例:
ObjectAnimator.ofFloat(view, "rotationY", 0.0f, 360.0f).setDuration(1000).start();
<4 设置btn 属性属性
ObjectAnimator.ofInt(btn,"width", 300).setDuration(4000).start();
setWidth方法不是设置view的宽度,所以上面属性动画无效。
Static class TargetView{
View target;
TargetView(Viewview)
{
target=view;
}
public int getWidth()
{
return target.getLayoutParams().width;
}
public void setWidth(int width)
{
target.getLayoutParams().width=width;
target.requestLayout();
}
}
TargetView targetView =new TargetView(btn);
ObjectAnimator.ofInt(targetView,"width", 300).setDuration(4000).start();
例2:
public class ObjectAnimatorFragment extends Fragment implements OnClickListener{
private ListView mListViewFront;
private ListView mListViewReverse;
private Button mButtonFlip;
private Button mButtonAlpha;
private Button mButtonScale;
private Button mButtonTranslate;
private Button mButtonRotate;
private Button mButtonSet;
private ImageView mImageView;
private int screenWidth = 0;
private int screenHeight = 0;
String[] frontStrs = {
"Front Page 1",
"Front Page 2",
"Front Page 3",
"Front Page 4",
"Front Page 5",
"Front Page 6",
};
String[] reverseStrs = {
"Reverse Page 1",
"Reverse Page 2",
"Reverse Page 3",
"Reverse Page 4",
"Reverse Page 5",
"Reverse Page 6",
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
float density = metrics.density;
//screenWidth = (int)(metrics.widthPixels * density + 0.5f);
//screenHeight = (int)(metrics.heightPixels * density + 0.5f);
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View rootView = inflater.inflate(R.layout.fragment_objectanimator, container, false);
mListViewFront = (ListView) rootView.findViewById(R.id.front_page_listview);
mListViewReverse = (ListView) rootView.findViewById(R.id.reverse_page_listview);
mButtonFlip = (Button)rootView.findViewById(R.id.button_flip);
mButtonFlip.setOnClickListener(this);
mButtonAlpha = (Button)rootView.findViewById(R.id.button_alpha);
mButtonAlpha.setOnClickListener(this);
mButtonScale = (Button)rootView.findViewById(R.id.button_scale);
mButtonScale.setOnClickListener(this);
mButtonTranslate = (Button)rootView.findViewById(R.id.button_translate);
mButtonTranslate.setOnClickListener(this);
mButtonRotate = (Button)rootView.findViewById(R.id.button_rotate);
mButtonRotate.setOnClickListener(this);
mButtonSet = (Button)rootView.findViewById(R.id.button_set);
mButtonSet.setOnClickListener(this);
mImageView = (ImageView)rootView.findViewById(R.id.objectanimator_imageview);
mImageView.setOnClickListener(this);
initData();
return rootView;
}
public void initData(){
ArrayAdapter<String> frontListData = new ArrayAdapter<String>(getActivity(), R.layout.layout_objectanimator_item,R.id.objectanimtor_item_textview, frontStrs);
ArrayAdapter<String> reverseListData = new ArrayAdapter<String>(getActivity(), R.layout.layout_objectanimator_item,R.id.objectanimtor_item_textview, reverseStrs);
mListViewFront.setAdapter(frontListData);
mListViewReverse.setAdapter(reverseListData);
mListViewReverse.setRotationX(-90.0f);
}
@Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button_flip:
flip();
break;
case R.id.button_alpha:
alphaAnimator();
break;
case R.id.button_scale:
scaleAnimator();
break;
case R.id.button_translate:
translateAniamtor();
break;
case R.id.button_rotate:
rotateAniamtor();
break;
case R.id.button_set:
setAnimator();
break;
case R.id.objectanimator_imageview:
mListViewFront.setVisibility(View.VISIBLE);
mImageView.setVisibility(View.GONE);
break;
default:
break;
}
}
/**
* 翻转动画效果
*/
public void flip(){
final ListView visibleView;
final ListView invisibleView;
if(mListViewFront.getVisibility() == View.GONE){
visibleView = mListViewReverse;
invisibleView = mListViewFront;
}else{
visibleView = mListViewFront;
invisibleView = mListViewReverse;
}
//创建ListView从Visible到Gone的动画
ObjectAnimator visibleToInVisable = ObjectAnimator.ofFloat(visibleView, "rotationX", 0.0f,90.0f);
//设置插值器
visibleToInVisable.setInterpolator(new AccelerateInterpolator());
visibleToInVisable.setDuration(500);
//创建ListView从Gone到Visible的动画
final ObjectAnimator invisibleToVisible = ObjectAnimator.ofFloat(invisibleView, "rotationX", -90.0f,0.0f);
//设置插值器
invisibleToVisible.setInterpolator(new DecelerateInterpolator());
invisibleToVisible.setDuration(500);
visibleToInVisable.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
super.onAnimationEnd(animation);
visibleView.setVisibility(View.GONE);
invisibleToVisible.start();
invisibleView.setVisibility(View.VISIBLE);
}
});
visibleToInVisable.start();
}
/**
* 渐变动画效果
*/
public void alphaAnimator(){
ListView alphaListView = null;
if(mListViewFront.getVisibility() == View.GONE){
alphaListView = mListViewReverse;
}else{
alphaListView = mListViewFront;
}
//1、通过调用ofFloat()方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;
ObjectAnimator mAnimatorAlpha = ObjectAnimator.ofFloat(alphaListView, "alpha", 1.0f,0.0f);
//2、设置动画的持续时间、是否重复及重复次数属性;
mAnimatorAlpha.setRepeatMode(Animation.REVERSE);
mAnimatorAlpha.setRepeatCount(3);
mAnimatorAlpha.setDuration(1000);
//3、启动动画
mAnimatorAlpha.start();
}
/**
* 伸缩动画效果
*/
public void scaleAnimator(){
ListView scaleListView = null;
if(mListViewFront.getVisibility() == View.GONE){
scaleListView = mListViewReverse;
}else{
scaleListView = mListViewFront;
}
ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(scaleListView, "scaleX", 1.0f,0.0f);
mAnimatorScaleX.setRepeatMode(Animation.REVERSE);
mAnimatorScaleX.setRepeatCount(3);
mAnimatorScaleX.setDuration(1000);
ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(scaleListView, "scaleY", 1.0f,0.0f);
mAnimatorScaleY.setRepeatMode(Animation.REVERSE);
mAnimatorScaleY.setRepeatCount(3);
mAnimatorScaleY.setDuration(1000);
mAnimatorScaleX.start();
mAnimatorScaleY.start();
}
/**
* 位移动画效果
*/
public void translateAniamtor(){
ListView translateListView = null;
if(mListViewFront.getVisibility() == View.GONE){
translateListView = mListViewReverse;
}else{
translateListView = mListViewFront;
}
ObjectAnimator mAnimatorTranslateX = ObjectAnimator.ofFloat(translateListView, "translationX", 0.0f,screenWidth/2);
mAnimatorTranslateX.setRepeatMode(Animation.REVERSE);
mAnimatorTranslateX.setRepeatCount(3);
mAnimatorTranslateX.setDuration(1000);
ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(translateListView, "translationY", 0.0f,screenHeight/2);
mAnimatorTranslateY.setRepeatMode(Animation.REVERSE);
mAnimatorTranslateY.setRepeatCount(3);
mAnimatorTranslateY.setDuration(1000);
mAnimatorTranslateX.start();
mAnimatorTranslateY.start();
}
/**
* 旋转动画效果
*/
public void rotateAniamtor(){
ListView rotateListView = null;
if(mListViewFront.getVisibility() == View.GONE){
rotateListView = mListViewReverse;
}else{
rotateListView = mListViewFront;
}
ObjectAnimator mAnimatorRotate = ObjectAnimator.ofFloat(rotateListView, "rotation", 0.0f,360.0f);
mAnimatorRotate.setRepeatMode(Animation.REVERSE);
mAnimatorRotate.setRepeatCount(2);
mAnimatorRotate.setDuration(2000);
mAnimatorRotate.start();
}
/**
* 动画集合
*/
public void setAnimator(){
ListView setListView = null;
if(mListViewFront.getVisibility() == View.GONE){
setListView = mListViewReverse;
}else{
setListView = mListViewFront;
}
setListView.setVisibility(View.GONE);
if(mImageView.getVisibility() == View.GONE){
mImageView.setVisibility(View.VISIBLE);
}
//代码方式设置动画
codeAnimatorSet(mImageView);
//用ViewPropertyAnimator实现动画
//viewPropertyAnimator(setListView);
//加载XML文件中的动画
/*AnimatorSet mAnimatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(getActivity(), R.animator.property_animation_animatorset);
mAnimatorSet.setTarget(mImageView);
mAnimatorSet.start();*/
}
/**
* 使用编码方式实现动画效果
* @param mImageView
*/
public void codeAnimatorSet(ImageView mImageView){
AnimatorSet mAnimatorSet = new AnimatorSet();
ObjectAnimator mAnimatorSetRotateX = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f,360.0f);
mAnimatorSetRotateX.setDuration(3000);
ObjectAnimator mAnimatorSetRotateY = ObjectAnimator.ofFloat(mImageView, "rotationY", 0.0f,360.0f);
mAnimatorSetRotateY.setDuration(3000);
ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(mImageView, "scaleX", 1.0f,0.5f);
mAnimatorScaleX.setRepeatCount(1);
mAnimatorScaleX.setRepeatMode(Animation.REVERSE);
mAnimatorScaleX.setDuration(1500);
ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(mImageView, "scaleY", 1.0f,0.5f);
mAnimatorScaleY.setRepeatCount(1);
mAnimatorScaleY.setRepeatMode(Animation.REVERSE);
mAnimatorScaleY.setDuration(1500);
mAnimatorSet.play(mAnimatorSetRotateY).with(mAnimatorScaleX);
mAnimatorSet.play(mAnimatorScaleX).with(mAnimatorScaleY);
mAnimatorSet.play(mAnimatorSetRotateY).before(mAnimatorSetRotateX);
mAnimatorSet.start();
}
public void viewPropertyAnimator(ListView mListViewHolder){
mListViewHolder.animate().cancel();
mListViewHolder.animate().rotationX(360.0f).setDuration(3000).start();
}
}
<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"
android:background="@color/view_animation_background"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="翻转"
android:id="@+id/button_flip"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Alpha"
android:id="@+id/button_alpha"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Scale"
android:id="@+id/button_scale"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Translate"
android:id="@+id/button_translate"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rotate"
android:id="@+id/button_rotate"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Set"
android:id="@+id/button_set"
/>
</LinearLayout>
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="@color/propertyanimator_background"
android:dividerHeight="0.5dp"
android:id="@+id/front_page_listview"
/>
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="@color/propertyanimator_background"
android:visibility="gone"
android:dividerHeight="0.5dp"
android:id="@+id/reverse_page_listview"
/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitStart"
android:visibility="visible"
android:src="@drawable/liduohai"
android:id="@+id/objectanimator_imageview"
/>
</LinearLayout>
如果要多次改变动画效果,可以直接设置keyFrame:
例3:
//前半段从0变到360度,后半段从360度变回0度
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000ms);
例4:动画修改背景色
private static final int RED = 0xffFF8080;
private static final int BLUE = 0xff8080FF;
private static final int CYAN = 0xff80ffff;
private static final int GREEN = 0xff80ff80;
//动画 变色
ObjectAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", CYAN, BLUE, RED);
colorAnim.setTarget(ll_animation);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.setDuration(3000);
colorAnim.start();
3.6 PropertyValuesHolder使用:
多属性动画同时工作管理类。有时候我们需要同时修改多个属性,那就可以用到此类,具体如下:
PropertyValuesHolder a1 = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
PropertyValuesHolder a2 = PropertyValuesHolder.ofFloat("translationY", 0, viewWidth);
......
ObjectAnimator.ofPropertyValuesHolder(view, a1, a2, ......).setDuration(1000).start();
如上代码就可以实现同时修改多个属性的动画啦
PS:
PropertyValuesHolder类为目标属性名来设置属性值的初始值和结束值,
然后ValueAnimator通过调用ofPropertyValuesHolder(PropertyValuesHolder...values)设置已经配置好的PropertyValuesHolder对象。
不过需要注意的是使用PropertyValuesHolder设置的属性必须是目标对象的属性中有setXX()方法才能进行设置,
例如:ImageView对象中有setScaleX()方法才能为ImageView设置对应属性的PropertyValuesHolder对象。
例如:
/**
* 使用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();
}
/**
* 使用ValueAnimator实现图片缩放动画
*/
public void scaleValueAnimator(){
//1.设置目标属性名及属性变化的初始值和结束值
PropertyValuesHolder mPropertyValuesHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f,0.0f);
PropertyValuesHolder mPropertyValuesHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f,0.0f);
ValueAnimator mAnimator = ValueAnimator.ofPropertyValuesHolder(mPropertyValuesHolderScaleX,mPropertyValuesHolderScaleY);
//2.为目标对象的属性变化设置监听器
mAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 3.根据属性名获取属性变化的值分别为ImageView目标对象设置X和Y轴的缩放值
float animatorValueScaleX = (float) animation.getAnimatedValue("scaleX");
float animatorValueScaleY = (float) animation.getAnimatedValue("scaleY");
mImageViewTest.setScaleX(animatorValueScaleX);
mImageViewTest.setScaleY(animatorValueScaleY);
}
});
//4.为ValueAnimator设置自定义的Interpolator
mAnimator.setInterpolator(new CustomInterpolator());
//5.设置动画的持续时间、是否重复及重复次数等属性
mAnimator.setDuration(2000);
mAnimator.setRepeatCount(3);
mAnimator.setRepeatMode(ValueAnimator.REVERSE);
//6.为ValueAnimator设置目标对象并开始执行动画
mAnimator.setTarget(mImageViewTest);
mAnimator.start();
}
public class CustomInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
input *= 0.8f;
return input * input;
}
}
/**
* 隐藏或显示ListView的动画
*/
public void hideOrShowListViewAnimator(final int startValue,final int endValue){
//1.设置属性的初始值和结束值
ValueAnimator mAnimator = ValueAnimator.ofInt(0,100);
//2.为目标对象的属性变化设置监听器
mAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
int animatorValue = (int)animation.getAnimatedValue();
float fraction = animatorValue/100f;
IntEvaluator mEvaluator = new IntEvaluator();
//3.使用IntEvaluator计算属性值并赋值给ListView的高
mListView.getLayoutParams().height = mEvaluator.evaluate(fraction, startValue, endValue);
mListView.requestLayout();
}
});
//4.为ValueAnimator设置LinearInterpolator
mAnimator.setInterpolator(new LinearInterpolator());
//5.设置动画的持续时间
mAnimator.setDuration(500);
//6.为ValueAnimator设置目标对象并开始执行动画
mAnimator.setTarget(mListView);
mAnimator.start();
}
3.7 AnimationSet的使用方法
动画集合,提供把多个动画组合成一个组合的机制,并可设置动画的时序关系,
如同时播放、顺序播放或延迟播放。具体使用方法比较简单,如下:
例1:
ObjectAnimator a1 = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0f);
ObjectAnimator a2 = ObjectAnimator.ofFloat(view, "translationY", 0f, viewWidth);
......
AnimatorSet animSet = new AnimatorSet();
animSet.setDuration(5000);
animSet.setInterpolator(new LinearInterpolator());
//animSet.playTogether(a1, a2, ...); //两个动画同时执行
animSet.play(a1).after(a2); //先后执行
......//其他组合方式
animSet.start();
例2:
AnimatorSet set =new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(animatorSet,"scaleX", 1,1.5f),
ObjectAnimator.ofFloat(animatorSet,"scaleY", 1,1.5f),
ObjectAnimator.ofFloat(animatorSet,"alpha", 1,0.25f,1)
);
set.setDuration(4000).start();
3.8 Evaluators相关类解释
使用TypeEvaluator根据插值因子计算属性值,Android系统可识别的类型包括int、float和颜色,
分别由 IntEvaluator、 FloatEvaluator、 ArgbEvaluator 提供支持,如下
Evaluators就是属性动画系统如何去计算一个属性值。它们通过Animator提供的动画的起始和结束值去计算一个动画的属性值。
IntEvaluator:整数属性值。
FloatEvaluator:浮点数属性值。
ArgbEvaluator:十六进制color属性值。
TypeEvaluator:用户自定义属性值接口,譬如对象属性值类型不是int、float、color类型,你必须实现这个接口去定义自己的数据类型。
既然说到这了,那就来个例子吧,譬如我们需要实现一个自定义属性类型和计算规则的属性动画,如下类型float[]:
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(5000);
valueAnimator.setObjectValues(new float[2]); //设置属性值类型
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator<float[]>()
{
@Override
public float[] evaluate(float fraction, float[] startValue,
float[] endValue)
{
//实现自定义规则计算的float[]类型的属性值
float[] temp = new float[2];
temp[0] = fraction * 2;
temp[1] = (float)Math.random() * 10 * fraction;
return temp;
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float[] xyPos = (float[]) animation.getAnimatedValue();
view.setHeight(xyPos[0]); //通过属性值设置View属性动画
view.setWidth(xyPos[1]); //通过属性值设置View属性动画
}
});
如果系统提供的TypeEvaluator不能满足我们的要求,我们也可以通过继承TypeEvaluator类来定义自己的Evaluator,
在自定义的TypeEvaluator类中只需要要重写getInterpolation(float input)方法,并在该方法通过相关的计算,
再返回计算后的结果(插值因子)就搞定,具体示例如下:
public class CustomEvaluator implements TypeEvaluator<Number> {
@Override
public Float evaluate(float fraction, Number startValue, Number endValue) {
// TODO Auto-generated method stub
float propertyResult = 0;
/*float startFloat = startValue.floatValue();
return (startFloat + fraction * (endValue.floatValue() - startFloat));*/
return propertyResult;
}
}
3.9 Interpolators相关类解释:
AccelerateDecelerateInterolator:先加速后减速。
AccelerateInterpolator:加速。
DecelerateInterpolator:减速。
AnticipateInterpolator:先向相反方向改变一段再加速播放。
AnticipateOvershootInterpolator:先向相反方向改变,再加速播放,会超出目标值然后缓慢移动至目标值,类似于弹簧回弹。
BounceInterpolator:快到目标值时值会跳跃。
CycleIinterpolator:动画循环一定次数,值的改变为一正弦函数:Math.sin(2 * mCycles * Math.PI * input)。
LinearInterpolator:线性均匀改变。
OvershottInterpolator:最后超出目标值然后缓慢改变到目标值。
TimeInterpolator:一个允许自定义Interpolator的接口,以上都实现了该接口。
举个例子,就像系统提供的标准API一样,如下就是加速插值器的实现代码,我们自定义时也可以类似实现:
//开始很慢然后不断加速的插值器。
public class AccelerateInterpolator implements Interpolator {
private final float mFactor;
private final double mDoubleFactor;
public AccelerateInterpolator() {
mFactor = 1.0f;
mDoubleFactor = 2.0;
}
......
//input 0到1.0。表示动画当前点的值,0表示开头,1表示结尾。
//return 插值。值可以大于1超出目标值,也可以小于0突破低值。
@Override
public float getInterpolation(float input) {
//实现核心代码块
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
}
综上可以发现,我们可以使用现有系统提供标准的东东实现属性动画,
也可以通过自定义继承相关接口实现自己的动画,只要实现上面提到的那些主要方法即可。
3.10 Java属性动画拓展之ViewPropertyAnimator动画
(1) View 类中新增的便于实现 property 动画的属性( ** ):
<1 translationX 和 translationY:这两个属性控制着 View 的屏幕位置坐标变化量,以 layout 容器的左上角为坐标原点;
<2 rotation、rotationX 和 rotationY:这三个属性控制着 2D 旋转角度(rotation属性)和围绕某枢轴点的 3D 旋转角度;
<3 scaleX、scaleY:这两个属性控制着 View 围绕某枢轴点的 2D 缩放比例;
<4 pivotX 和 pivotY: 这两个属性控制着枢轴点的位置,前述的旋转和缩放都是以此点为中心展开的,缺省的枢轴点是 View 对象的中心点;
<5 x 和 y:这是指 View 在容器内的最终位置,等于 View 左上角相对于容器的坐标加上 translationX 和 translationY 后的值;
<6 alpha:表示 View 的 alpha 透明度。缺省值为 1 (不透明),为 0 则表示完全透明(看不见);
要动画显示 View 对象的某个属性,比如颜色或旋转值,我们所有要做的事情就是创建一个 Property animation,并设定对应的 View 属性。那接下来我们就用ObjectAnimator类来分别实现View的透明度渐变、收缩、移动和旋转等动画效果,那在此之前我们也来总结下使用ObjectAnimator实现动画的几个步骤,如下:
1.通过调用ofFloat()、ofInt()等方法创建ObjectAnimator对象,并设置目标对象、需要改变的目标属性名、初始值和结束值;
2.设置动画的持续时间、是否重复及重复次数等属性;
3.启动动画
在Android API 12时,View中添加了animate方法,具体如下:
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
......
/**
* This method returns a ViewPropertyAnimator object, which can be used to animate
* specific properties on this View.
*
* @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
*/
public ViewPropertyAnimator animate() {
if (mAnimator == null) {
mAnimator = new ViewPropertyAnimator(this);
}
return mAnimator;
}
......
}
可以看见通过View的animate()方法可以得到一个ViewPropertyAnimator的属性动画(有人说他没有继承Animator类,是的,他是成员关系,不是之前那种继承关系)。
ViewPropertyAnimator提供了一种非常方便的方法为View的部分属性设置动画(切记,是部分属性),它可以直接使用一个 Animator对象设置多个属性的动画;在多属性设置动画时,它比 上面的ObjectAnimator更加牛逼、高效,因为他会管理多个属性的invalidate方法统一调运触发,而不像上面分别调用,所以还会有一些 性能优化。如下就是一个例子:
例1:
<1 myView.animate().x(0f).y(100f).start();
<2 view.animate()
.alpha(0).y(300)
.setDuration(300)
.withStartAction(new Runnable() {
@Override
public void run() {
}
})
.withEndAction(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
})
.start();
例2:
ViewPropertyAnimator animator = View.animate();
animator.x(50f);
animator.start();
3.11 Java属性动画拓展之LayoutAnimator容器布局动画
Property动画系统还提供了对ViewGroup中View添加时的动画功能,我们可以用LayoutTransition对 ViewGroup中的View进行动画设置显示。
LayoutTransition的动画效果都是设置给ViewGroup,然后当被设置动画的 ViewGroup中添加删除View时体现出来。
该类用于当前布局容器中有View添加、删除、隐藏、显示等时候定义布局容器自身的动画和View的动画,
也就是说当在一个LinerLayout中隐藏一个View的时候,我们可以自定义 整个由于LinerLayout隐藏View而改变的动画,
同时还可以自定义被隐藏的View自己消失时候的动画等。
我们可以发现LayoutTransition类中主要有五种容器转换动画类型,具体如下:
LayoutTransition.APPEARING:当View出现或者添加的时候View出现的动画。
LayoutTransition.CHANGE_APPEARING:当添加View导致布局容器改变的时候整个布局容器的动画。
LayoutTransition.DISAPPEARING:当View消失或者隐藏的时候View消失的动画。
LayoutTransition.CHANGE_DISAPPEARING:当删除或者隐藏View导致布局容器改变的时候整个布局容器的动画。
LayoutTransition.CHANGE:当不是由于View出现或消失造成对其他View位置造成改变的时候整个布局容器的动画。
LayoutAnimator示例
例1:
XML方式使用系统提供的默认LayoutTransition动画:
我们可以通过如下方式使用系统提供的默认ViewGroup的LayoutTransition动画:
android:animateLayoutChanges=”true”
在ViewGroup添加如上xml属性默认是没有任何动画效果的,因为前面说了,该动画针对于ViewGroup内部东东发生改变时才有效,
所以当我们设置如上属性然后调运ViewGroup的addView、removeView方法时就能看见系统默认的动画效果了。
例2还有一种就是通过如下方式设置:
android:layoutAnimation=”@anim/customer_anim”
通过这种方式就能实现很多吊炸天的动画。
Java方式使用系统提供的默认LayoutTransition动画:
在使用LayoutTransition时,你可以自定义这几种事件类型的动画,也可以使用默认的动画,
总之最终都是通过 setLayoutTransition(LayoutTransition lt)方法把这些动画以一个LayoutTransition对象设置给一个ViewGroup。
譬如实现如上Xml方式的默认系统LayoutTransition动画如下:
mTransitioner = new LayoutTransition();
mViewGroup.setLayoutTransition(mTransitioner);
稍微再高端一点吧,我们来自定义这几类事件的动画,分别实现他们,那么你可以像下面这么处理:
mTransitioner = new LayoutTransition();
......
ObjectAnimator anim = ObjectAnimator.ofFloat(this, "scaleX", 0, 1);
......//设置更多动画
mTransition.setAnimator(LayoutTransition.APPEARING, anim);
......//设置更多类型的动画 mViewGroup.setLayoutTransition(mTransitioner);
例2:
/*
* ViewGroup中使用LayoutTransition 进行 监听布局的改变,而创建动画
* LayoutTransition.APPEARING 新增出现时
* CHANGE_APPEARING
* CHANGE_DISAPPEARING 子view消失时
* CHANGING
* ViewGroup布局中:android:animateLayoutChanges="true" 使用的默认的LayoutTransition制作动画
*/
LayoutTransition layoutTransition = new LayoutTransition();
layoutTransition.setDuration(5000);
layoutTransition.setAnimator(LayoutTransition.APPEARING, scaleAnimator);
ll_animation.setLayoutTransition(layoutTransition);
final TextView tv = new TextView(this);
tv.setWidth(100);
tv.setHeight(100);
tv.setText("中华人民共和国");
ll_animation.addView(tv);//对应type = APPEARING
ll_animation.postDelayed(new Runnable() {
@Override
public void run() {
ll_animation.removeView(tv);
}
}, 2000);
四、 Android动画总结View动画:
View动画只能够为View添加动画,如果想为非View对象添加动画须自己实现;且View动画支持的种类很少;尤其是他改变的是View的绘制效果,View的属性没有改变,其位置与大小都不变;
View动画代码量少,使用简单方便。
Property动画:
弥补了View动画的缺陷,你可以为一个对象的任意属性添加动画,对象自己的属性会被真的改变;当对象的属性变化的时候,
属性动画会自动刷新屏幕;属性动画改变的是对象的真实属性,而且属性动画不止用于View,还可以用于任何对象。
好了,太不容易了!这一篇文章差点难产了,断断续续各种事,终于追求完美OK了,接下来博客恢复正常更新。