时间:2021-07-01 10:21:17 帮助过:19人阅读
相关文章:
1、《Animation 动画详解(一)——alpha、scale、translate、rotate、set的xml属性及用法》
2、《Animation动画详解(二)——Interpolator插值器》
3、《Animation动画详解(三)—— 代码生成alpha、scale、translate、rotate、set及插值器动画》
4、《Animation动画详解(四)——ValueAnimator基本使用》
5、《Animation动画详解(五)——ValueAnimator高级进阶(一)》
6、《Animation动画详解(六)——ValueAnimator高级进阶(二)》
7、《Animation动画详解(七)——ObjectAnimator基本使用》
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);animator.setDuration(2000);animator.start();效果图如下:
我们这里还是直接使用上一篇的框架代码;(当点击start anim时执行动画)从上面的代码中可以看到构造ObjectAnimator的方法非常简单:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0);animator.setDuration(2000);animator.start();效果图如下:
从代码中可以看到,我们只需要改变ofFloat()的第二个参数的值就可以实现对应的动画;
那么问题来了,我们怎么知道第二个参数的值是啥呢?
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0);TextView控件有rotation这个属性吗?没有,不光TextView没有,连它的父类View中也没有这个属性。那它是怎么来改变这个值的呢?其实,ObjectAnimator做动画,并不是根据控件xml中的属性来改变的,而是通过指定属性所对应的set方法来改变的。比如,我们上面指定的改变rotation的属性值,ObjectAnimator在做动画时就会到指定控件(TextView)中去找对应的setRotation()方法来改变控件中对应的值。同样的道理,当我们在最开始的示例代码中,指定改变”alpha”属性值的时候,ObjectAnimator也会到TextView中去找对应的setAlpha()方法。那TextView中都有这些方法吗,有的,这些方法都是从View中继承过来的,在View中有关动画,总共有下面几组set方法:
//1、透明度:alphapublic void setAlpha(float alpha)//2、旋转度数:rotation、rotationX、rotationYpublic void setRotation(float rotation)public void setRotationX(float rotationX)public void setRotationY(float rotationY)//3、平移:translationX、translationYpublic void setTranslationX(float translationX) public void setTranslationY(float translationY)//缩放:scaleX、scaleYpublic void setScaleX(float scaleX)public void setScaleY(float scaleY)可以看到在View中已经实现了有关alpha,rotaion,translate,scale相关的set方法。所以我们在构造ObjectAnimator时可以直接使用。
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationX",0,270,0);animator.setDuration(2000);animator.start();
效果图如下:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationY",0,180,0);animator.setDuration(2000);animator.start();
效果图如下:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,270,0);animator.setDuration(2000);animator.start();
从这张图中,绿色框部分表示手机屏幕,很明显可以看出Z轴就是从屏幕左上角原点向外伸出的一条轴。这样,我们也就可以理解围绕Z轴旋转,为什么是这样子转了。
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationX", 0, 200, -200,0);animator.setDuration(2000);animator.start();效果图如下:
所以,我们上面在构造动画时,指定的移动距离是(0, 200, -200,0),所以控件会从自身所有位置向右移动200像素,然后再移动到距离原点-200的位置,最后回到原点;
然后我们来看看setTranslateY的用法:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "translationY", 0, 200, -100,0);animator.setDuration(2000);animator.start();效果图如下:(为了方便看到效果,将textview垂直居中)
同样,移动位置的坐标也都是以当前控件所在位置为中心点的。所以对应的移动位置从原点移动向下移动200像素,然后再移动到向下到距原点200像素的位置,最后再回到(0,0)从效果图中很明显可以看出来。
从上面可以看出:每次移动距离的计算都是以原点为中心的;比如初始动画为ObjectAnimator.ofFloat(tv, “translationY”, 0, 200, -100,0)表示首先从0移动到正方向200的位置,然后再移动到负方向100的位置,最后移动到原点。
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleX", 0, 3, 1);animator.setDuration(2000);animator.start();效果图如下:
在效果图中,从0倍放大到3倍,然后再还原到1倍的原始状态。
然后再来看看setScaleY的用法
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY", 0, 3, 1);animator.setDuration(2000);animator.start();为了更好的看到效果,我把textview垂直居中了,效果图如下:
源码在文章底部给出
好了,到这里有关View中自带的set函数讲完了,我们来看看ObjectAnimator是如何实现控件动画效果的。在这张图中,将ValueAnimator的动画流程与ObjectAnimator的动画流程做了个对比。
可以看到ObjectAnimator的动画流程中,也是首先通过加速器产生当前进度的百分比,然后再经过Evaluator生成对应百分比所对应的数字值。这两步与ValueAnimator是完全一样的,唯一不同的是最后一步,在ValueAnimator中,我们要通过添加监听器来监听当前数字值。而在ObjectAnimator中,则是先根据属性值拼装成对应的set函数的名字,比如这里的scaleY的拼装方法就是将属性的第一个字母强制大写后,与set拼接,所以就是setScaleY。然后通过反射找到对应控件的setScaleY(float scaleY)函数,将当前数字值做为setScaleY(float scale)的参数将其传入。
这里在找到控件的set函数以后,是通过反射来调用这个函数的,有关反射的使用大家可以参考《夯实JAVA基本之二 —— 反射(1):基本类周边信息获取》
这就是ObjectAnimator的流程,最后一步总结起来就是调用对应属性的set方法,将动画当前数字值做为参数传进去。
根据上面的流程,这里有几个注意事项:
(1)、拼接set函数的方法:上面我们也说了是首先是强制将属性的第一个字母大写,然后与set拼接,就是对应的set函数的名字。注意,只是强制将属性的第一个字母大写,后面的部分是保持不变的。反过来,如果我们的函数名命名为setScalePointX(float ),那我们在写属性时可以写成”scalePointX”或者写成“ScalePointX”都是可以的,即第一个字母大小写可以随意,但后面的部分必须与set方法后的大小写保持一致。
(2)、如何确定函数的参数类型:上面我们知道了如何找到对应的函数名,那对应的参数方法的参数类型如何确定呢?我们在讲ValueAnimator的时候说过,动画过程中产生的数字值与构造时传入的值类型是一样的。由于ObjectAnimator与ValueAnimator在插值器和Evaluator这两步是完全一样的,而当前动画数值的产生是在Evaluator这一步产生的,所以ObjectAnimator的动画中产生的数值类型也是与构造时的类型一样的。那么问题来了,像我们的构造方法。
ObjectAnimator animator = ObjectAnimator.ofFloat(tv, "scaleY", 0, 3, 1);由于构造时使用的是ofFloat函数,所以中间值的类型应该是Float类型的,所以在最后一步拼装出来的set函数应该是setScaleY(float xxx)的样式;这时,系统就会利用反射来找到setScaleY(float xxx)函数,并把当前的动画数值做为参数传进去。
意思就是对应函数的指定参数类型没有找到。
(3)、调用set函数以后怎么办?从ObjectAnimator的流程可以看到,ObjectAnimator只负责把动画过程中的数值传到对应属性的set函数中就结束了,注意传给set函数以后就结束了!set函数就相当我们在ValueAnimator中添加的监听的作用,set函数中的对控件的操作还是需要我们自己来写的。
那我们来看看View中的setScaleY是怎么实现的吧:
/** * 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); }}大家不必理解这一坨代码的意义,因为这些代码是需要读懂View的整体流程以后才能看得懂的,只需要跟着我的步骤来理解就行。这段代码总共分为两部分:第一步重新设置当前控件的参数,第二步调用Invalidate()强制重绘;
这个效果图与我们上篇自定义控件实现的效果差不多,这个控件中存在一个圆形,也是在动画时先将这个圆形放大,然后再将圆形还原。
public class Point { private int mRadius; public Point(int radius){ mRadius = radius; } public int getRadius() { return mRadius; } public void setRadius(int radius) { mRadius = radius; }}这个类很好理解,只有一个成员变量mRadius,表示圆的半径。
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(); }}在这段代码中,首先来看我们前面讲到的set函数:
void setPointRadius(int radius){ mPoint.setRadius(radius); invalidate();}第一点,这个set函数所对应的属性应该是pointRadius或者PointRadius。前面我们已经讲了第一个字母大小写无所谓,后面的字母必须保持与set函数完全一致。
@Overrideprotected 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);}在onDraw函数中,就是根据当前mPoint的半径值在(300,300)点外画一个圆;有关画圆的知识,大家可以参考 《android Graphics(一):概述及基本几何图形绘制》
布局代码很好理解,根据效果图中的布局效果来理解,非常容易,就不再多讲
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(); } }); } …………}在点击start anim按钮后,开始执行doPointViewAnimation()函数,doPointViewAnimation()函数代码如下:
private void doPointViewAnimation(){ ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius", 0, 300, 100); animator.setDuration(2000); animator.start();}在这段代码中,着重看ObjectAnimator的构造方法,首先要操作的控件对象是mPointView,然后对应的属性是pointRadius,然后值是从0到300再到100;
源码在文章底部给出
public void setBackgroundColor(int color);大家可以回想到,我们在ValueAnimator中也曾改变过背景色,使用的是ArgbEvaluator。在这里我们再回顾下ArgbEvaluator,它的实现代码如下:
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)))); }}有关它具体实现的原理,前面篇章中我们已经讲过了,这里主要说一点,ArgbEvaluator的返回值是Integer类型,所以我们要使用ArgbEvaluator的话,构造ObjectAnimator时必须使用ofInt()
ObjectAnimator animator = ObjectAnimator.ofInt(tv, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff);animator.setDuration(8000);animator.setEvaluator(new ArgbEvaluator());animator.start();然后我们来看下代码效果:
源码在文章底部给出
/** * 设置动画时长,单位是毫秒 */ValueAnimator setDuration(long duration)/** * 获取ValueAnimator在运动时,当前运动点的值 */Object getAnimatedValue();/** * 开始动画 */void start()/** * 设置循环次数,设置为INFINITE表示无限循环 */void setRepeatCount(int value)/** * 设置循环模式 * value取值有RESTART,REVERSE, */void setRepeatMode(int value)/** * 取消动画 */void cancel()
/** * 监听器一:监听动画变化时的实时值 */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)
/** * 设置插值器 */public void setInterpolator(TimeInterpolator value)/** * 设置Evaluator */public void setEvaluator(TypeEvaluator value)到这里,有关ObjectAnimator的知识就讲完了,下篇再讲讲联合动画和xml中实现动画的方法。
源码下载地址:
csdn:
github:
请大家尊重原创者版权,转载请标明出处,谢谢