本文共 15734 字,大约阅读时间需要 52 分钟。
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "alpha" , 1 , 0 , 1 ); animator.setDuration( 2000 ); animator.start(); |
我们这里还是直接使用上一篇的框架代码;(当点击start anim时执行动画)从上面的代码中可以看到构造ObjectAnimator的方法非常简单:
1 | public static ObjectAnimator ofFloat(Object target, String propertyName, float ... values) |
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "rotation" , 0 , 180 , 0 ); animator.setDuration( 2000 ); animator.start(); |
从代码中可以看到,我们只需要改变ofFloat()的第二个参数的值就可以实现对应的动画;
那么问题来了,我们怎么知道第二个参数的值是啥呢? 1 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "rotation" , 0 , 180 , 0 ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //1、透明度:alpha public void setAlpha( float alpha) //2、旋转度数:rotation、rotationX、rotationY public void setRotation( float rotation) public void setRotationX( float rotationX) public void setRotationY( float rotationY) //3、平移:translationX、translationY public void setTranslationX( float translationX) public void setTranslationY( float translationY) //缩放:scaleX、scaleY public void setScaleX( float scaleX) public void setScaleY( float scaleY) |
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "rotationX" , 0 , 270 , 0 ); animator.setDuration( 2000 ); animator.start(); |
效果图如下:
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "rotationY" , 0 , 180 , 0 ); animator.setDuration( 2000 ); animator.start(); |
效果图如下:
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "rotation" , 0 , 270 , 0 ); animator.setDuration( 2000 ); animator.start(); |
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationX" , 0 , 200 , - 200 , 0 ); animator.setDuration( 2000 ); animator.start(); |
所以,我们上面在构造动画时,指定的移动距离是(0, 200, -200,0),所以控件会从自身所有位置向右移动200像素,然后再移动到距离原点-200的位置,最后回到原点;
然后我们来看看setTranslateY的用法:
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationY" , 0 , 200 , - 100 , 0 ); animator.setDuration( 2000 ); animator.start(); |
同样,移动位置的坐标也都是以当前控件所在位置为中心点的。所以对应的移动位置从原点移动向下移动200像素,然后再移动到向下到距原点200像素的位置,最后再回到(0,0)从效果图中很明显可以看出来。
从上面可以看出:每次移动距离的计算都是以原点为中心的;比如初始动画为ObjectAnimator.ofFloat(tv, “translationY”, 0, 200, -100,0)表示首先从0移动到正方向200的位置,然后再移动到负方向100的位置,最后移动到原点。
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleX" , 0 , 3 , 1 ); animator.setDuration( 2000 ); animator.start(); |
在效果图中,从0倍放大到3倍,然后再还原到1倍的原始状态。
然后再来看看setScaleY的用法
1 2 3 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY" , 0 , 3 , 1 ); animator.setDuration( 2000 ); animator.start(); |
在这张图中,将ValueAnimator的动画流程与ObjectAnimator的动画流程做了个对比。
可以看到ObjectAnimator的动画流程中,也是首先通过加速器产生当前进度的百分比,然后再经过Evaluator生成对应百分比所对应的数字值。这两步与ValueAnimator是完全一样的,唯一不同的是最后一步,在ValueAnimator中,我们要通过添加监听器来监听当前数字值。而在ObjectAnimator中,则是先根据属性值拼装成对应的set函数的名字,比如这里的scaleY的拼装方法就是将属性的第一个字母强制大写后,与set拼接,所以就是setScaleY。然后通过反射找到对应控件的setScaleY(float scaleY)函数,将当前数字值做为setScaleY(float scale)的参数将其传入。 这里在找到控件的set函数以后,是通过反射来调用这个函数的,有关反射的使用大家可以参考 这就是ObjectAnimator的流程,最后一步总结起来就是调用对应属性的set方法,将动画当前数字值做为参数传进去。 根据上面的流程,这里有几个注意事项: (1)、拼接set函数的方法:上面我们也说了是首先是强制将属性的第一个字母大写,然后与set拼接,就是对应的set函数的名字。注意,只是强制将属性的第一个字母大写,后面的部分是保持不变的。反过来,如果我们的函数名命名为setScalePointX(float ),那我们在写属性时可以写成”scalePointX”或者写成“ScalePointX”都是可以的,即第一个字母大小写可以随意,但后面的部分必须与set方法后的大小写保持一致。 (2)、如何确定函数的参数类型:上面我们知道了如何找到对应的函数名,那对应的参数方法的参数类型如何确定呢?我们在讲ValueAnimator的时候说过,动画过程中产生的数字值与构造时传入的值类型是一样的。由于ObjectAnimator与ValueAnimator在插值器和Evaluator这两步是完全一样的,而当前动画数值的产生是在Evaluator这一步产生的,所以ObjectAnimator的动画中产生的数值类型也是与构造时的类型一样的。那么问题来了,像我们的构造方法。 1 | ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY" , 0 , 3 , 1 ); |
意思就是对应函数的指定参数类型没有找到。
(3)、调用set函数以后怎么办?从ObjectAnimator的流程可以看到,ObjectAnimator只负责把动画过程中的数值传到对应属性的set函数中就结束了,注意传给set函数以后就结束了!set函数就相当我们在ValueAnimator中添加的监听的作用,set函数中的对控件的操作还是需要我们自己来写的。那我们来看看View中的setScaleY是怎么实现的吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /** * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of * the view's unscaled width. A value of 1 means that no scaling is applied. * * @param scaleY The scaling factor. * @see #getPivotX() * @see #getPivotY() * * @attr ref android.R.styleable#View_scaleY */ public void setScaleY( float scaleY) { ensureTransformationInfo(); final TransformationInfo info = mTransformationInfo; if (info.mScaleY != scaleY) { invalidateParentCaches(); // Double-invalidation is necessary to capture view's old and new areas invalidate( false ); info.mScaleY = scaleY; info.mMatrixDirty = true ; mPrivateFlags |= DRAWN; // force another invalidation with the new orientation invalidate( false ); } } |
这个效果图与我们上篇自定义控件实现的效果差不多,这个控件中存在一个圆形,也是在动画时先将这个圆形放大,然后再将圆形还原。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Point { private int mRadius; public Point( int radius){ mRadius = radius; } public int getRadius() { return mRadius; } public void setRadius( int radius) { mRadius = radius; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class MyPointView extends View { private Point mPoint = new Point( 100 ); public MyPointView(Context context, AttributeSet attrs) { super (context, attrs); } @Override protected void onDraw(Canvas canvas) { if (mPoint != null ){ Paint paint = new Paint(); paint.setAntiAlias( true ); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawCircle( 300 , 300 ,mPoint.getRadius(),paint); } super .onDraw(canvas); } void setPointRadius( int radius){ mPoint.setRadius(radius); invalidate(); } } |
1 2 3 4 | void setPointRadius( int radius){ mPoint.setRadius(radius); invalidate(); } |
1 2 3 4 5 6 7 8 9 10 11 | @Override protected void onDraw(Canvas canvas) { if (mPoint != null ){ Paint paint = new Paint(); paint.setAntiAlias( true ); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawCircle( 300 , 300 ,mPoint.getRadius(),paint); } super .onDraw(canvas); } |
1 2 3 4 | <relativelayout android:layout_height= "fill_parent" android:layout_width= "fill_parent" android:orientation= "vertical" xmlns:android= "" ><button android:id= "@+id/btn" android:layout_alignparentleft= "true" android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:padding= "10dp" android:text= "start anim" type= "submit" ></button><button android:id= "@+id/btn_cancel" android:layout_alignparentright= "true" android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:padding= "10dp" android:text= "cancel anim" type= "submit" ></button><button android:id= "@+id/btn" android:layout_alignparentleft= "true" android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:padding= "10dp" android:text= "start anim" type= "submit" > <textview android:background= "#ffff00" android:gravity= "center" android:id= "@+id/tv" android:layout_centerhorizontal= "true" android:layout_height= "wrap_content" android:layout_width= "100dp" android:padding= "10dp" android:text= "Hello qijian" > <com.example.blogobjectanimator1.mypointview android:id= "@+id/pointview" android:layout_below= "@id/tv" android:layout_height= "match_parent" android:layout_width= "match_parent" ></com.example.blogobjectanimator1.mypointview></textview></button></relativelayout> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class MyActivity extends Activity { private Button btnStart; private MyPointView mPointView; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); btnStart = (Button) findViewById(R.id.btn); mPointView = (MyPointView)findViewById(R.id.pointview); btnStart.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { doPointViewAnimation(); } }); } ………… } |
1 2 3 4 5 | private void doPointViewAnimation(){ ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius" , 0 , 300 , 100 ); animator.setDuration( 2000 ); animator.start(); } |
1 | public void setBackgroundColor( int color); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class ArgbEvaluator implements TypeEvaluator { public Object evaluate( float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24 ); int startR = (startInt >> 16 ) & 0xff ; int startG = (startInt >> 8 ) & 0xff ; int startB = startInt & 0xff ; int endInt = (Integer) endValue; int endA = (endInt >> 24 ); int endR = (endInt >> 16 ) & 0xff ; int endG = (endInt >> 8 ) & 0xff ; int endB = endInt & 0xff ; return ( int )((startA + ( int )(fraction * (endA - startA))) << 24 ) | ( int )((startR + ( int )(fraction * (endR - startR))) << 16 ) | ( int )((startG + ( int )(fraction * (endG - startG))) << 8 ) | ( int )((startB + ( int )(fraction * (endB - startB)))); } } |
1 2 3 4 | ObjectAnimator animator = ObjectAnimator.ofInt(tv, "BackgroundColor" , 0xffff00ff , 0xffffff00 , 0xffff00ff ); animator.setDuration( 8000 ); animator.setEvaluator( new ArgbEvaluator()); animator.start(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /** * 设置动画时长,单位是毫秒 */ ValueAnimator setDuration( long duration) /** * 获取ValueAnimator在运动时,当前运动点的值 */ Object getAnimatedValue(); /** * 开始动画 */ void start() /** * 设置循环次数,设置为INFINITE表示无限循环 */ void setRepeatCount( int value) /** * 设置循环模式 * value取值有RESTART,REVERSE, */ void setRepeatMode( int value) /** * 取消动画 */ void cancel() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /** * 监听器一:监听动画变化时的实时值 */ public static interface AnimatorUpdateListener { void onAnimationUpdate(ValueAnimator animation); } //添加方法为:public void addUpdateListener(AnimatorUpdateListener listener) /** * 监听器二:监听动画变化时四个状态 */ public static interface AnimatorListener { void onAnimationStart(Animator animation); void onAnimationEnd(Animator animation); void onAnimationCancel(Animator animation); void onAnimationRepeat(Animator animation); } //添加方法为:public void addListener(AnimatorListener listener) |
1 2 3 4 5 6 7 8 | /** * 设置插值器 */ public void setInterpolator(TimeInterpolator value) /** * 设置Evaluator */ public void setEvaluator(TypeEvaluator value) |