由于Android3.0之前已有的动画框架Animation存在依稀局限性---动画改变的只是显示,并不能响应事件。 因此3.0之后Google就提出了属性动画这样一个新的动画框架,帮助开发者实现更加丰富的动画效果。
属性动画中最基本的ObjectAnimator.
在Animator框架中使用最多的就是AnimatorSet和ObjectAnimator配合。
使用ObjectAnimator进行更精细化控制,只控制一个对象的一个属性值。
而使用多个ObjectAnimator组合到AnimatorSet形成一个动画。而且ObjectAnimator能够自动驱动,可以调用setFrameDelay(longframeDelay)设置动画帧之间的间隙时间,
调整帧率,减少动画过程中频繁绘制界面,而在不影响动画效果的前提下减少CPU资源消耗。最重要的是,属性动画通过调用属性的get、set方法来真实地控制了一个View的属性值,
因此强大的属性动画框架,基本可以实现所有的动画效果。
平移动画代码:
int count = 0; btn_objectanimation.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { int step = count*10+10; ObjectAnimator oa = ObjectAnimator.ofFloat( btn_objectanimation, "translationX", step ); oa.setDuration(1000); oa.start(); count++; } });
在使用ObjectAnimator的时候,有一点非常重要,那就是要操作的属性必须具有get、set方法,不然ObjectAnimator就无法起效。
下面就是一些常用的可以直接使用属性动画的属性值:
如果一个属性没有get、set方法,可以通过自定义一个属性类或者包装类,来间接地给这个属性增加get、set方法,或者通过ValueAnimator来实现。
通过自定义属性类代码如下:
private static class WrapperView{ private View mTarget; public WrapperView(View target){ this.mTarget = target; } public int getWidth(){ return mTarget.getLayoutParams().width; } public void setWidth(int width){ mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } }
使用时只需要操纵包装类就可以间接调用到get、set方法了:
WrapperView wrapperView = new WrapperView(btn_objectanimation); ObjectAnimator.ofInt(wrapperView, "width", 500).setDuration(5000).start();
PropertyValuesHolder
类似视图动画中的AnimationSet,在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现。
例子:平移动画+改变x、y轴的缩放:
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300f); PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f); PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f); ObjectAnimator.ofPropertyValuesHolder(view, pvh1, pvh2, pvh3).setDuration(1000).start();
同时可以使用AnimatorSet完成上面多个动画效果:
代码:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 300f); ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0, 1f); ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0, 1f); AnimatorSet set = new AnimatorSet(); set.setDuration(1000); set.playTogether(animator1, animator2, animator3); set.start();
在AnimatorSet中,正是通过playTogether()、playSequentially()、animSet.play().with()、before()、after()这些方法来控制多个动画的协同工作方式,从而做到对动画播放顺序的精确控制。
ValueAnimator
ValueAnimator在属性动画中占有非常重要的地位,虽然不像ObjectAnimator那样耀眼,但它却是属性动画的核心所在,ObjectAnimator也是集成自ValueAnimator.
public final class ObjectAnimator extends ValueAnimator
ValueAnimator的一般使用方法如下所示,通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值的变换,从而完成动画的变换。
ValueAnimator animator = ValueAnimator.ofFloat(0, 100); animator.setTarget(view); animator.setDuration(1000).start(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { Float value = (Float)valueAnimator.getAnimatedValue(); Log.e("value", value+""); TranslateAnimation ta = new TranslateAnimation(0, value, 0, 0); ta.setDuration(1000); btn_objectanimation.startAnimation(ta); } });
日志如下:
02-17 13:10:02.240 13420-13420/com.jiarong.animationdemo E/value: 0.0 02-17 13:10:02.256 13420-13420/com.jiarong.animationdemo E/value: 0.06315112 02-17 13:10:02.276 13420-13420/com.jiarong.animationdemo E/value: 0.26845932 02-17 13:10:02.292 13420-13420/com.jiarong.animationdemo E/value: 0.61558187 02-17 13:10:02.308 13420-13420/com.jiarong.animationdemo E/value: 1.0709554 02-17 13:10:02.324 13420-13420/com.jiarong.animationdemo E/value: 1.6901821 02-17 13:10:02.340 13420-13420/com.jiarong.animationdemo E/value: 2.447176 02-17 13:10:02.356 13420-13420/com.jiarong.animationdemo E/value: 3.2835484 02-17 13:10:02.372 13420-13420/com.jiarong.animationdemo E/value: 4.3014555 02-17 13:10:02.392 13420-13420/com.jiarong.animationdemo E/value: 5.4496737 02-17 13:10:02.408 13420-13420/com.jiarong.animationdemo E/value: 6.6464663 02-17 13:10:02.420 13420-13420/com.jiarong.animationdemo E/value: 8.037975 02-17 13:10:02.440 13420-13420/com.jiarong.animationdemo E/value: 9.549156 02-17 13:10:02.456 13420-13420/com.jiarong.animationdemo E/value: 11.076882 02-17 13:10:02.476 13420-13420/com.jiarong.animationdemo E/value: 12.807748 02-17 13:10:02.488 13420-13420/com.jiarong.animationdemo E/value: 14.644662 02-17 13:10:02.504 13420-13420/com.jiarong.animationdemo E/value: 16.465723 02-17 13:10:02.520 13420-13420/com.jiarong.animationdemo E/value: 18.493307 02-17 13:10:02.540 13420-13420/com.jiarong.animationdemo E/value: 20.610731 02-17 13:10:02.556 13420-13420/com.jiarong.animationdemo E/value: 22.680279 02-17 13:10:02.572 13420-13420/com.jiarong.animationdemo E/value: 24.954662 02-17 13:10:02.588 13420-13420/com.jiarong.animationdemo E/value: 27.300476 02-17 13:10:02.608 13420-13420/com.jiarong.animationdemo E/value: 29.567537 02-17 13:10:02.624 13420-13420/com.jiarong.animationdemo E/value: 32.032734 02-17 13:10:02.640 13420-13420/com.jiarong.animationdemo E/value: 34.54915 02-17 13:10:02.660 13420-13420/com.jiarong.animationdemo E/value: 36.957924 02-17 13:10:02.676 13420-13420/com.jiarong.animationdemo E/value: 39.5532 02-17 13:10:02.688 13420-13420/com.jiarong.animationdemo E/value: 42.178284 02-17 13:10:02.708 13420-13420/com.jiarong.animationdemo E/value: 44.669437 02-17 13:10:02.724 13420-13420/com.jiarong.animationdemo E/value: 47.33092 02-17 13:10:02.740 13420-13420/com.jiarong.animationdemo E/value: 50.0 02-17 13:10:02.756 13420-13420/com.jiarong.animationdemo E/value: 52.512222 02-17 13:10:02.776 13420-13420/com.jiarong.animationdemo E/value: 55.174343 02-17 13:10:02.792 13420-13420/com.jiarong.animationdemo E/value: 57.821716 02-17 13:10:02.808 13420-13420/com.jiarong.animationdemo E/value: 60.293125 02-17 13:10:02.824 13420-13420/com.jiarong.animationdemo E/value: 62.89036 02-17 13:10:02.840 13420-13420/com.jiarong.animationdemo E/value: 65.45085 02-17 13:10:02.856 13420-13420/com.jiarong.animationdemo E/value: 67.82059 02-17 13:10:02.876 13420-13420/com.jiarong.animationdemo E/value: 70.288994 02-17 13:10:02.892 13420-13420/com.jiarong.animationdemo E/value: 72.699524 02-17 13:10:02.908 13420-13420/com.jiarong.animationdemo E/value: 74.909256 02-17 13:10:02.924 13420-13420/com.jiarong.animationdemo E/value: 77.18804 02-17 13:10:02.940 13420-13420/com.jiarong.animationdemo E/value: 79.38927 02-17 13:10:02.956 13420-13420/com.jiarong.animationdemo E/value: 81.384575 02-17 13:10:02.976 13420-13420/com.jiarong.animationdemo E/value: 83.4176 02-17 13:10:02.992 13420-13420/com.jiarong.animationdemo E/value: 85.35534 02-17 13:10:03.008 13420-13420/com.jiarong.animationdemo E/value: 87.0871 02-17 13:10:03.020 13420-13420/com.jiarong.animationdemo E/value: 88.824326 02-17 13:10:03.036 13420-13420/com.jiarong.animationdemo E/value: 90.45084 02-17 13:10:03.056 13420-13420/com.jiarong.animationdemo E/value: 91.876396 02-17 13:10:03.076 13420-13420/com.jiarong.animationdemo E/value: 93.275055 02-17 13:10:03.092 13420-13420/com.jiarong.animationdemo E/value: 94.55032 02-17 13:10:03.104 13420-13420/com.jiarong.animationdemo E/value: 95.63458 02-17 13:10:03.124 13420-13420/com.jiarong.animationdemo E/value: 96.66023 02-17 13:10:03.140 13420-13420/com.jiarong.animationdemo E/value: 97.552826 02-17 13:10:03.156 13420-13420/com.jiarong.animationdemo E/value: 98.26908 02-17 13:10:03.176 13420-13420/com.jiarong.animationdemo E/value: 98.89647 02-17 13:10:03.188 13420-13420/com.jiarong.animationdemo E/value: 99.384415 02-17 13:10:03.208 13420-13420/com.jiarong.animationdemo E/value: 99.71504 02-17 13:10:03.224 13420-13420/com.jiarong.animationdemo E/value: 99.92871 02-17 13:10:03.240 13420-13420/com.jiarong.animationdemo E/value: 100.0
动画事件的监听
一个完整的动画具有Start、Repeat、End、Cancel四个过程,通过Android提供的接口,可以很方便地监听到这四个事件,代码如下所示:
ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", 0.5f); anim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); anim.start();
大部分时候,我们都只关心onAnimationEnd事件,所以Android也提供了一个AnimatorListenerAdapter来让我们选择必要的事件进行监听,代码如下:
anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); } });
属性动画写在XML中,
代码如下所示:注意该文件位置如图所示:你没看错,是在animator资源文件夹下
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="2.0" android:valueType="floatType"> </objectAnimator>
使用方法:
Animator anim = AnimatorInflater.loadAnimator(MainActivity.this, R.animator.scalex); anim.setTarget(view); anim.start();
View的animate方法
在Android3.0之后,Google给View增加了animate方法来直接驱动属性动画,代码如下所示:
可以发现,其实animate方法可以认为是属性动画的一种简写方式:
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();
模拟电视机关闭的效果,让一个图片纵向比例不断缩小
public class CustomTV extends Animation { private int mCenterWidth; private int mCenterHeight; private Camera camera; private float mRotateY = 0.0f; @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); //设置默认时长 setDuration(2000); //动画结束后保留状态 setFillAfter(true); //设置默认插值器 setInterpolator(new BounceInterpolator()); mCenterWidth = width / 2; mCenterHeight = height / 2; } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final Matrix matrix = t.getMatrix(); matrix.preScale(1, 1 - interpolatedTime, mCenterWidth, mCenterHeight); } }
//电视机缩放动画 ImageView mImageView = (ImageView)findViewById(R.id.mImageView); mImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { CustomTV customTV = new CustomTV(); view.startAnimation(customTV); } });
自定义动画:
public class CustomAnimation extends Animation { private int mCenterWidth; private int mCenterHeight; private Camera mCamera = new Camera(); private float mRotateY = 0.0f; @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); // 设置默认时长 setDuration(2000); // 动画结束后保留状态 setFillAfter(true); // 设置默认插值器 setInterpolator(new BounceInterpolator()); mCenterWidth = width / 2; mCenterHeight = height / 2; } //设置旋转角度 public void setRotateY(float rotateY){ mRotateY = rotateY; } /** * * @param interpolatedTime 插值器的时间因子,这个因子由当前动画当前完成的百分比 和 当前时间所对应的插值所计算得来,取值范围为0到1.0 * @param t 矩阵的封装类,一般使用这个类来获得当前的矩阵对象 */ @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final Matrix matrix = t.getMatrix(); mCamera.save(); //使用Camera设置旋转的角度 mCamera.rotateY(mRotateY * interpolatedTime); //将旋转变换作用到matrix上 mCamera.getMatrix(matrix); mCamera.restore(); //通过pre方法设置矩阵作用前的偏移量来改变旋转中心 matrix.preTranslate(mCenterWidth, mCenterHeight); matrix.postTranslate(-mCenterWidth, -mCenterHeight); } }
ImageView mImageViewRotate = (ImageView)findViewById(R.id.mImageViewRotate); mImageViewRotate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { CustomAnimation customAnimation = new CustomAnimation(); customAnimation.setRotateY(30); view.startAnimation(customAnimation); } });