上节对Android的属性动画进行了初步的学习,相信 对于属性动画已经不再是一知半解的状态了,本节继续来探究Android属性动画的更高级的用法!依旧贴下郭神的三篇文章~
Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法
Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法
内容依旧是参考的上述三篇文章, 开始本节内容~
1.Evaluator自定义
1)Evaluator介绍
上一节中的8.4.3 Android动画合集之属性动画-初见,使用动画的第一步都是
调用ValueAnimator的ofInt(),ofFloat()或ofObject()静态方法创建ValueAnimator实例!
在例子中,ofInt和ofFloat都用到了,分别用于对浮点型和整型的数据进行动画操作!
那么ofObject()?初始对象和结束对象?如何过渡法?或者说这玩意怎么用?
带着疑问,先来了解个东西Evaluator,在属性动画概念叨叨逼处其实就说到了这个东西
用来告诉动画系统如何从初始值过渡到结束值! 的入手点没错!进去IntEvaluator的源码,看下里面写了些什么?
实现了TypeEvaluator接口,然后重写了evaluate()方法,参数有三个,依次是
- fraction动画的完成度,根据他来计算动画的值应该是多少
- startValue动画的起始值
- endValue动画的结束值
动画的值 = 初始值 + 完成度 * (结束值 - 初始值)
同样的还有FloatEvaluator,想告诉系统如何从初始对象过度到结束对象,那么就要自己来实现TypeEvaluator接口,即自定义Evaluator了,说多无益,写个例子来看看
2)使用示例
运行效果图
代码实现
定义个对象Point.java,对象中只有x,y两个属性以及get,set方法~
/** * Created by Jay on 2015/11/18 0018. */ public class Point { private float x; private float y; public Point() { } public Point(float x, float y) { this.x = x; this.y = y; } public float getX() { return x; } public float getY() { return y; } public void setX(float x) { this.x = x; } public void setY(float y) { this.y = y; } }
接着自定义Evaluator类PointEvaluator.java,实现接口重写evaluate方法~
/** * Created by Jay on 2015/11/18 0018. */ public class PointEvaluator implements TypeEvaluator<Point>{ @Override public Point evaluate(float fraction, Point startValue, Point endValue) { float x = startValue.getX() + fraction * (endValue.getX() - startValue.getX()); float y = startValue.getY() + fraction * (endValue.getY() - startValue.getY()); Point point = new Point(x, y); return point; } }
然后自定义个View类AnimView.java,很简单~
/** * Created by Jay on 2015/11/18 0018. */ public class AnimView extends View { public static final float RADIUS = 80.0f; private Point currentPoint; private Paint mPaint; public AnimView(Context context) { this(context, null); } public AnimView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public AnimView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLUE); } private void drawCircle(Canvas canvas){ float x = currentPoint.getX(); float y = currentPoint.getY(); canvas.drawCircle(x, y, RADIUS, mPaint); } private void startAnimation() { Point startPoint = new Point(RADIUS, RADIUS); Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS); ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { currentPoint = (Point) animation.getAnimatedValue(); invalidate(); } }); anim.setDuration(3000l); anim.start(); } @Override protected void onDraw(Canvas canvas) { if (currentPoint == null) { currentPoint = new Point(RADIUS, RADIUS); drawCircle(canvas); startAnimation(); } else { drawCircle(canvas); } } }
最后MainActivity.java处实例化这个View即可~
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new AnimView(this)); } }
3)示例增强版
上面示例的基础上加上圆移动时的颜色变化~这里另外用个ObjectAnimator来加载颜色变化的动画,在View中加多个int color来控制颜色,另外写上getColor()和setColor()的方法,先来自定义个Evaluator吧~
运行效果图
实现代码
ColorEvaluator.java
/** * Created by Jay on 2015/11/18 0018. */ public class ColorEvaluator implements TypeEvaluator<Integer>{ @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int alpha = (int) (Color.alpha(startValue) + fraction * (Color.alpha(endValue) - Color.alpha(startValue))); int red = (int) (Color.red(startValue) + fraction * (Color.red(endValue) - Color.red(startValue))); int green = (int) (Color.green(startValue) + fraction * (Color.green(endValue) - Color.green(startValue))); int blue = (int) (Color.blue(startValue) + fraction * (Color.blue(endValue) - Color.blue(startValue))); return Color.argb(alpha, red, green, blue); } }
然后自定义View那里加个color,get和set方法;创建个ObjectAnimator,和AnimatorSet,接着把动画组合到一起就到,这里就加点东西而已,怕 有问题,直接另外建个View吧~
AnimView2.java
/** * Created by Jay on 2015/11/18 0018. */ public class AnimView2 extends View { public static final float RADIUS = 80.0f; private Point currentPoint; private Paint mPaint; private int mColor; public AnimView2(Context context) { this(context, null); } public AnimView2(Context context, AttributeSet attrs) { super(context, attrs); init(); } public AnimView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLUE); } private void drawCircle(Canvas canvas){ float x = currentPoint.getX(); float y = currentPoint.getY(); canvas.drawCircle(x, y, RADIUS, mPaint); } private void startAnimation() { Point startPoint = new Point(RADIUS, RADIUS); Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS); ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { currentPoint = (Point) animation.getAnimatedValue(); invalidate(); } }); ObjectAnimator objectAnimator = ObjectAnimator.ofObject(this, "color", new ColorEvaluator(), Color.BLUE, Color.RED); //动画集合将前面两个动画加到一起,with同时播放 AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(anim).with(objectAnimator); animatorSet.setStartDelay(1000l); animatorSet.setDuration(3000l); animatorSet.start(); } @Override protected void onDraw(Canvas canvas) { if (currentPoint == null) { currentPoint = new Point(RADIUS, RADIUS); drawCircle(canvas); startAnimation(); } else { drawCircle(canvas); } } //color的get和set方法~ public int getColor() { return mColor; } public void setColor(int color) { mColor = color; mPaint.setColor(color); invalidate(); } }
然后MainActivity,setContentView那里把AnimView改成AnimView2就好~
2.Interpolator(补间器)
在讲补间动画的时候就讲过这个东东了~不知道 还有印象没?
上面的补间器补间动画和属性动画都可用,且补间动画还新增了个TimeInterpolator接口该接口是用于兼容之前的Interpolator的,这使得所有过去的Interpolator实现类都可以直接拿过来放到属性动画当中使用!可以调用动画对象的setInterpolator()方法设置不同的Interpolator!先该点东西,让小球从屏幕正中央的顶部掉落到底部~然后会为的集合动画调用下述语句animatorSet.setInterpolator(new AccelerateInterpolator(2f));括号里的值用于控制加速度~
运行效果
好像有点不和常理,正常应该是会弹起来的吧,换成BounceInterpolator试试~
嘿嘿,效果蛮赞的,当然还有N多个系统提供好的Interpolator, 可以自己一一尝试,这里就不慢慢跟 纠结了~
下面来看看
1)Interpolator的内部实现机制
先到TimeInterpolator接口的源码,发现这里只有个getInterpolation()方法;
简单的解释getInterpolation()方法中接收个input参数,这个参数的值会随着动画的运行而不断变化,不过它的变化是非常有规律的,就是根据设定的动画时长匀速增加,变化范围是0到1 也就是说当动画一开始的时候input的值是0,到动画结束的时候input的值是1,而中间的值则是随着动画运行的时长在0到1之间变化的
这里的input值决定了TypeEvaluator接口里的fraction的值 input的值是由系统经过计算后传入到getInterpolation()方法中的,然后可以自己实现getInterpolation()方法中的算法,根据input的值来计算出个返回值,而这个返回值就是fraction了
可以看看LinearInterpolator里的代码
这里没有处理过直接返回input值,即fraction的值就是等于input的值,这就是匀速运动的Interpolator的实现方式!其实无非就是算法不同,这就涉及到数学的东西了,又一次体会到数学的重要性了,这里再贴个BounceInterpolator的源码吧
别问这里的算法,也不知道哈,再找个容易理解点的AccelerateDecelerateInterpolator
这个Interpolator是先加速后减速效果的(float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f 的算法理解
解由input的取值范围为[0,1],可以得出cos中的值的取值范围为[π,2π],对应的值为-1和1;再用这个值来除以2加上0.5之后,getInterpolation()方法最终返回的结果值范围还是[0,1],对应的曲线图如下
所以是个先加速后减速的过程!
学渣没法玩了...,上面全是郭大叔文章里搬过来的...想静静...
2)自定义Interpolator
好吧,还是等会儿再忧伤吧,写个自定义的Interpolator示例先 实现TimeInterpolator接口,重写getInterpolation方法
示例代码如下
private class DecelerateAccelerateInterpolator implements TimeInterpolator { @Override public float getInterpolation(float input) { if (input < 0.5) { return (float) (Math.sin(input * Math.PI) / 2); } else { return 1 - (float) (Math.sin(input * Math.PI) / 2); } } }
调用setInterpolator(new DecelerateAccelerateInterpolator())设置下即可~限于篇幅就不贴图了~
3.ViewPropertyAnimator
3.1后系统当中附增的个新的功能,为View的动画操作提供一种更加便捷的用法!假如是以前,让个TextView从正常状态变成透明状态,会这样写
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 0f); animator.start();
而使用ViewPropertyAnimator来实现同样的效果则显得更加易懂
textview.animate().alpha(0f);
还支持连缀用法,组合多个动画,设定时长,设置Interpolator等~
textview.animate().x(500).y(500).setDuration(5000) .setInterpolator(new BounceInterpolator());
用法很简单,使用的时候查下文档就好~,另外下面有几个细节的地方要注意一下!
- 整个ViewPropertyAnimator的功能都是建立在View类新增的animate()方法之上的,这个方法会创建并返回个ViewPropertyAnimator的实例,之后的调用的所有方法,设置的所有属性都是通过这个实例完成的
- 使用ViewPropertyAnimator将动画定义完成之后,动画就会自动启动 并且这个机制对于组合动画也同样有效,只要不断地连缀新的方法,那么动画就不会立刻执行,等到所有在ViewPropertyAnimator上设置的方法都执行完毕后,动画就会自动启动 当然若不想使用这一默认机制的话,也可以显式地调用start()方法来启动动画
- ViewPropertyAnimator的所有接口都是使用连缀的语法来设计的,每个方法的返回值都是它自身的实例,因此调用完个方法之后可以直接连缀调用它的另个方法,这样把所有的功能都串接起来,甚至可以仅通过一行代码就完成任意复杂度的动画功能
4.本节示例代码下载
在Github上找到个动画合集的项目,很多动画效果都有,下面贴下地址
想研究各种动画是如何实现的可自行查看源码~
本节小结
本节讲了稍微高深一点的东西Evaluator啊,Interpolator啊,还有ViewPropertyAnimator,是不是又拓展了 的见识~本节也是Android基础入门绘图与的最后一小节了,若 把这一章节的内容都掌握了,再去学自定义控件,或者看别人写的自定义控件,应该不会再那么地不知道从何入手,遇到一堆新面孔了吧!
还是谢谢郭神的文章,属性动画部分的内容很都是直接在郭神那里搬过来的
嘿嘿~本节就到这里,
PS:后面的示意图换模拟器是因为的N5秀逗了...
尊贵的董事大人
英文标题不为空时 视为本栏投稿
需要关键字 描述 英文标题