Property Animation - 实解


属性动画实解


Android动画系统为我们定义好了一些主要的Animator,Evaluator和Interpolator。在大部分的情况下我们只需要使用这些就足够了,大家可以在android.view.animation 包中进行了解


使用ValueAnimator和ObjectAnimator来进行动画


可以使用ValueAnimator类指定属性动画过程中要改变的属性值的一个集合范围,比如alpha属性,它的数据类型是float,就可以使用ValueAnimator.ofFloat(startValue,endValue)方法来设置一个渐变的范围。ValueAnimator类还可以设置动画持续的时间setDuration(time)

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();

上面的代码片段并不能实际工作,还需获取到不同时间点属性对应的值,从而应用到某个View的属性上,只需设置一个Animator监听器即可,代码如下

			final ImageView ball = (ImageView) findViewById(R.id.ball);
			
			ValueAnimator valueAnim = ValueAnimator.ofFloat(0f, 1.0f);
			valueAnim.addUpdateListener(new AnimatorUpdateListener() {
				
				@Override
				public void onAnimationUpdate(ValueAnimator animation) {
					float alpha = (Float) animation.getAnimatedValue();
					ball.setAlpha(alpha);
				}
			});
			
			valueAnim.setDuration(3000);
			valueAnim.start();


为了简化ValueAnimator的工作,Android动画系统提供了ObjectAnimator类,它继承于ValueAnimator,可以直接应用于一个对象的一个属性上,而不用像ValueAnimator去设置一个动画监听器来监听属性值的改变,具体代码如下

			final ImageView ball = (ImageView) findViewById(R.id.ball);
			ObjectAnimator objAnim = ObjectAnimator.ofFloat(ball, "alpha", 0f, 1.0f);
			objAnim.setDuration(3000);
			objAnim.start();

代码相应的简化很多,也更好理解,但如果想让ObjectAnimator工作正常的话,有一些需要注意的点

1、对应对象的属性上得有相应的setter方法

2、如果ObjectAnimator.ofType(obj, proName, values...) 可变参数values为一个时,那么相应的属性还得有getter方法,因为当只有一个values参数时,那么这个参数则为endValue,动画系统要使用属性的getter方法来获取属性的startValue

3、setter和getter方法对应属性的类型必须得一样

4、对于动画应用的对象或属性的不同,你可能需要在应用的对象上调用invalidate()方法来使对象使用最新的属性时来在屏幕上重绘自己,比如Drawable对象,color动画属性在Drawable对象上并没有相应的setter方法,而invalidate()方法是在setter方法上进行调用的,这时你就需手动的调用invalidate()方法了,对于动画属性在对应的对象上有setter方法的就没这个必要了。那在什么时机手动调用invalidate()方法呢,一般只需在属性值改变的回调方法中调用即可(AnimatorUpdateListener)


使用AnimatorSet进行组合动画

有的时候可能需要让几个动画组合起来,有顺序的或一起播放,这时使用AnimatorSet来进行组合动画就比较方便,下面是个小例子,渐变过后沿着水平方向移动

			ImageView ball = (ImageView) findViewById(R.id.ball);
			AnimatorSet animSet = new AnimatorSet();
			ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(ball, "alpha", 0f, 1.0f);
			alphaAnim.setDuration(2000);
			ObjectAnimator xAnim = ObjectAnimator.ofFloat(ball, "x", ball.getLeft(), 300f);
			xAnim.setDuration(3000);
			animSet.play(alphaAnim).before(xAnim);
			animSet.start();

其实还有很多种动画的组合方式,有兴趣的话可以在API DOC中查看AnimatorSet的说明

动画监听器介绍

我们可以使用动画监听器来监听动画的生命周期,监听属性值的改变等。使用Animator.AnimatorListener 来监听动画的生命周期,是我们能在动画不同的生命周期来做相应的处理

			final ImageView ball = (ImageView) findViewById(R.id.ball);
			ObjectAnimator objAnim = ObjectAnimator.ofFloat(ball, "alpha", 0f, 1.0f);
			objAnim.addListener(new Animator.AnimatorListener() {
				
				@Override
				public void onAnimationStart(Animator animation) {
					// TODO Auto-generated method stub
					Log.i(TAG, "Animation Start...");
				}
				
				@Override
				public void onAnimationRepeat(Animator animation) {
					// TODO Auto-generated method stub
					Log.i(TAG, "Animation Repeat...");
				}
				
				@Override
				public void onAnimationEnd(Animator animation) {
					// TODO Auto-generated method stub
					Log.i(TAG, "Animation End...");
				}
				
				@Override
				public void onAnimationCancel(Animator animation) {
					// TODO Auto-generated method stub
					Log.i(TAG, "Animation Cancel...");
				}
			});
			objAnim.setDuration(3000);
			objAnim.start();

使用 ValueAnimator.AnimatorUpdateListener  能获取到动画每一帧属性的值,这个值由ValueAnimator计算得出,可以使用ValueAnimator的实例方法getAnimatedValue()获得,这个监听器前面已经使用过了,这里就不介绍具体的使用方法了


当你只想监听动画的几个生命周期时,这时你可以使用AnimatorListenerAdapter,具体使用方法如下

			final ImageView ball = (ImageView) findViewById(R.id.ball);
			ObjectAnimator objAnim = ObjectAnimator.ofFloat(ball, "alpha", 0f, 1.0f);
			objAnim.addListener(new AnimatorListenerAdapter() {
				@Override
				public void onAnimationStart(Animator animation) {
					Log.i(TAG, "Animation Start...");
				}
			});
			objAnim.setDuration(3000);
			objAnim.start();

 View容器布局改变动画

Android属性动画系统为View容器布局的改变(容器里面的View的添加、移除、可见性改变等)也提供了动画支持,当有View添加进容器时,添加的View会有一个动画的效果,当有View从容器中移除时,移除的View会有一个消失的效果,Android属性动画系统为我们提供了四种默认的动画效果,分别是:APPEARING、CHANGE_APPEARING、DISAPPEARING和CHANGE_DISAPPEARING。为一个View容器指定这些默认的效果很简单,其实默认的动画效果是在 layout_animations_by_default.xml  中定义的,有兴趣的朋友可以去源码中进行查看,具体使用方式如下

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

如果想用自己的动画效果,需使用LayoutTransition设置相应的Animator,View容器使用setLayoutTransition( LayoutTransition)来设置相应的 LayoutTransition,具体代码如下

			LinearLayout v_ll = (LinearLayout) findViewById(R.id.verticalContainer);
			LayoutTransition layoutT = new LayoutTransition();
			ValueAnimator customAppearingAnim = ObjectAnimator.ofFloat(null, "rotationY", 90f, 0f);
			customAppearingAnim.setDuration(layoutT.getDuration(LayoutTransition.APPEARING));
			customAppearingAnim.addListener(new AnimatorListenerAdapter() {
				@Override
				public void onAnimationEnd(Animator animation) {
	                View view = (View) ((ObjectAnimator) animation).getTarget();
	                view.setRotationY(0f);
				}
			});
			
			layoutT.setAnimator(LayoutTransition.APPEARING, customAppearingAnim);
			v_ll.setLayoutTransition(layoutT);
			
			Button bt1 = new Button(mContext);
			bt1.setText("Button1");
			v_ll.addView(bt1);

自定义Evaluator和Interpolator

有些时候我们可能会使用到系统提供的Evaluator(IntEvaluator、FloatEvaluator、ArgbEvaluator)之外的Evalutor,这时可以继承TypeEvaluator来定义自己的Evaluator,下面是FloatEvaluator的定义

	public class FloatEvaluator implements TypeEvaluator {
		/**
		 * fraction为Interpolator计算的实际完成分数(根据时间分数和使用的Interpolator计算得出)
		 * 公式:startValue + fraction * (endValue - startValue)
		 * */
	    public Object evaluate(float fraction, Object startValue, Object endValue) {
	        float startFloat = ((Number) startValue).floatValue();
	        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
	    }
	}


Interpolator是用来干嘛的呢,实际上是用来根据一定的规则来计算某一时间点动画完成的百分比,再把这一个实际完成的百分比(fraction)传递给设定好的 Evaluator,由Evaluator来计算当前时间点上属性的值,属性动画系统为我们提供了一些基本的Interpolator,像LinearInterpolator和AccelerateDecelerateInterpolator,它们的定义如下

LinearInterpolator

public float getInterpolation(float input) {
    return input;
}

AccelerateDecelerateInterpolator

public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

如果想实现自己的Interpolator话,需实现TimeInterpolator

	public class MyInterpolator implements TimeInterpolator{
		@Override
		public float getInterpolation(float input) {
			return yourValue;
		}
	}

在特定的时间指定特定的值(Keyframe)

在属性动画的播放过程中如何在某一特定的时间让其具有一个特定的值(比如在中间快速移动),这时可以使用keyframe,它是个key-value结构,key指定时间值,value指定在该时间点上的值,下面是一个小例子,更复杂的Demo请看API DEMO中的MultiPropertyAnimation 例子程序 

			ImageView ball = (ImageView) findViewById(R.id.ball);
			Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
			Keyframe kf1 = Keyframe.ofFloat(0.5f, 80f);
			Keyframe kf2 = Keyframe.ofFloat(1f, 300f);
			PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("x", kf0, kf1, kf2);
			ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(ball, pvhRotation);
			rotationAnim.setDuration(5000);
			rotationAnim.start();

View动画

View动画的工作方式跟属性动画的工作方式有点不一样,View动画是以改变View本身的绘制方式来工作的,而View的绘制一般是由它的容器来负责,这就导致一个问题,虽然View在它容器中的显示效果改变了,但View本身的任何属性都没有发生过变化,比如说View在屏幕上显示的位置变化了,但View的x或y属性并没有发生变化,也就是说View还在它的原始位置,这就可能导致View不能响应click事件。属性动画就不一样了,它改变的就是View实际的属性值,并且在setter方法中会调用invalidate()方法更新View在屏幕上的显示,对于这点不清楚的朋友可以去看看自定义View的原理

如何来进行View动画,其实上面的例子基本上都是View动画,模式差不多如下

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);


使用ValuePropertyAnimator来进行动画

ValuePropertyAnimator提供了一种更简单的方式来平行的进行多个属性动画,ValuePropertyAnimator比较类似于ObjectAnimator,因为它也是改变的View的实际属性值,但它的工作方式更简明明了,也更高效,下面是几种实现方式

Multiple ObjectAnimator objects

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

One ObjectAnimator

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();

ViewPropertyAnimator

myView.animate().x(50f).y(100f);

关于ValuePropertyAnimator更详细的介绍请看这篇博文 http://android-developers.blogspot.com/2011/05/introducing-viewpropertyanimator.html (需翻墙)

 

在xml中定义动画

Android属性动画系统让我们可以在xml中来声明定义动画,替代直接编写代码的方式,这样可以更好的重复利用编写好的动画,往往也更加直观。在Android 3.1之前是在res/anim/目录下定义动画文件,但在Android 3.1后我们可以在res/animator/目录下来定义动画文件,在res/animator/下的动画文件可以让Android的可视化工具搜索和识别,直接就可以看到效果啦,但在res/animator/目录下定义是一个可选的操作

Animator类于xml标签对应如下,和一个animation xml样例

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

在代码中使用这个animation xml文件,在动画之前需设定好应用的对象

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.anim.property_animator);
set.setTarget(myObject);
set.start();

关于这个知识点更详细的信息详见 http://developer.android.com/guide/topics/resources/animation-resource.html#Property

参考:http://developer.android.com/guide/topics/graphics/prop-animation.html





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值