Android属性动画--TypeEvaluator
来源:互联网 发布:推荐知乎有趣的话题 编辑:程序博客网 时间:2024/05/22 08:27
1、功能介绍
可能在大多数情况下我们使用属性动画的时候都不会用到TypeEvaluator,因为属性动画已经比补间动画强大了许多,基本使用已经不成问题。但如果我们遇到了难以解决的问题的时候,使用 TypeEvaluator 可能会有意想不到的效果。
TypeEvaluator 的意思是估值器,所以它的作用就是告诉动画系统如何从初始值过度到结束值。我们学习 ValueAnimator 的时候学到的ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过渡,那么这个平滑过渡是怎么做到的呢?
其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,TypeEvaluator 是个接口,只有一个evaluate()方法。它用来返回你要进行动画的那个属性在当前时间点所需要的属性值。我们来看看 FloatEvaluator 的代码实现:
public class FloatEvaluator implements TypeEvaluator<Number> { public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); }}
可以看到,FloatEvaluator实现了TypeEvaluator接口,然后重写evaluate()方法。evaluate()方法当中传入了三个参数,第一个参数fraction非常重要,这个参数用于表示动画的完成度的,可以简单地理解成animator当前播放的进度条的位置。我们应该根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。
那么上述代码的逻辑就比较清晰了,用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。
2、Interpolator 与 Evaluator
这里我再提下 Interpolator 与 Evaluator 的关系。要知道Interpolator 的意思是插值器,而 Evaluator 则是估值器:
- Interpolator:用来定义animator变化的速率。它让基础的动画效果(渐变、拉伸、平移、旋转)有加速、减速、重复等效果。在源代码中,其实interpolation的作用就是根据某一个时间点来计算它的播放时间分数(fraction)
- evaluator:evaluator 全部继承与 TypeEvaluator接口,它只有一个 evaluate()方法,用来返回你要进行动画的那个属性在当前时间点所需要的属性值。
我们可以把动画的过程想象成是一部电影的播放,电影的播放中有进度条,Interpolator就是用来控制电影播放频率,也就是快进快退要多少倍速。然后Evaluator根据Interpolator提供的值计算当前播放电影中的哪一个画面,也就是进度条要处于什么位置。
因为 fraction 是由 Interpolator 计算出来的,所以它们两个是密不可分的。
3、自定义TypeEvaluator
我们使用过了ValueAnimator的ofFloat()和ofInt()方法,分别用于对浮点型和整型的数据进行动画操作的,但实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过度。
这里我的 View 是一个小球,我们来实现给它做抛物线运动,首先就为它写个实体类。
public class Point { private float x; private float y; public Point(float x, float y) { this.x = x; this.y = y; } public float getX() { return x; } public float getY() { return y; } }
这里 x,y用于记录坐标的位置,并提供了构造方法来设置坐标,以及get方法来获取坐标。
public class PointEvaluator implements TypeEvaluator { @Override public Object evaluate(float fraction, Object startValue, Object endValue) { float x = 200 * fraction * 3 + 32; float y =0.5f * 200 * (fraction * 3) * (fraction * 3) + 32; Point point = new Point(x, y); return point; }}
这就是我们自定义的 TypeEvaluator,因为x,y这两个坐标的单位是像素,而我的布局中设置了padding,值为16dp,而我的设备是320dpi的,所以换算成像素是32像素点,所以x,y的初始值为32。
这里要做的是抛物线,我定义的 x 方向上的速度为 200px/s,因为在后面会看到我把 Duration 设置为3秒,所以要对 fraction 这个进度值乘3才是正确的变化的时间。x=vt,y=1/2 * gt ^2,这个重力加速度设置为200px/s^2,这样就得到了变化的小球。
imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), new Point(0,0)); animator.setInterpolator(new LinearInterpolator()); animator.setDuration(3000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Point point = (Point) animation.getAnimatedValue(); v.setX(point.getX()); v.setY(point.getY()); } }); animator.start(); }});
因为我们在自定义 TypeEvaluator 中没有用到startValue 和 endValue,所以在 ofObject() 中传入的 Point 对象设置的值并没有作用。
这里同样可以将 Evaluator 和 ObjectValues 分开写:
ValueAnimator animator = new ValueAnimator();animator.setObjectValues(new Point(0, 0));animator.setEvaluator(new PointEvaluator());
这样效果是一样的。
自定义 TypeEvaluator 的大致流程就是这样,如果你对 ValueAnimator 产生的值不满意的话,自定义 TypeEvaluator 不失为一个好办法。
4、动态改变View颜色
我们在介绍 Property Animation时,免不了拿它与 Tweened Animations 去比较,补间动画只能实现移动、缩放、旋转和淡入淡出这四种动画操作,基本上没有任何扩展性。所以我们想要实现对View的颜色进行动态改变,补间动画是没有办法做到的。
这次我们的自定义TypeEvaluator是改变字符串color的值,这里我们可以将color属性设置为字符串类型,使用#RRGGBB这种格式来表示颜色值,代码如下:
public class ColorEvaluator implements TypeEvaluator { private int mCurrentRed = -1; private int mCurrentGreen = -1; private int mCurrentBlue = -1; @Override public Object evaluate(float fraction, Object startValue, Object endValue) { String startColor = (String) startValue; String endColor = (String) endValue; int startRed = Integer.parseInt(startColor.substring(1, 3), 16); int startGreen = Integer.parseInt(startColor.substring(3, 5), 16); int startBlue = Integer.parseInt(startColor.substring(5, 7), 16); int endRed = Integer.parseInt(endColor.substring(1, 3), 16); int endGreen = Integer.parseInt(endColor.substring(3, 5), 16); int endBlue = Integer.parseInt(endColor.substring(5, 7), 16); // 初始化颜色的值 if (mCurrentRed == -1) { mCurrentRed = startRed; } if (mCurrentGreen == -1) { mCurrentGreen = startGreen; } if (mCurrentBlue == -1) { mCurrentBlue = startBlue; } // 计算初始颜色和结束颜色之间的差值 int redDiff = Math.abs(startRed - endRed); int greenDiff = Math.abs(startGreen - endGreen); int blueDiff = Math.abs(startBlue - endBlue); int colorDiff = redDiff + greenDiff + blueDiff; if (mCurrentRed != endRed) { mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0, fraction); } else if (mCurrentGreen != endGreen) { mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff, redDiff, fraction); } else if (mCurrentBlue != endBlue) { mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff, redDiff + greenDiff, fraction); } // 将计算出的当前颜色的值组装返回 String currentColor = "#" + getHexString(mCurrentRed) + getHexString(mCurrentGreen) + getHexString(mCurrentBlue); return currentColor; } /** * 根据fraction值来计算当前的颜色。 */ private int getCurrentColor(int startColor, int endColor, int colorDiff, int offset, float fraction) { int currentColor; if (startColor > endColor) { currentColor = (int) (startColor - (fraction * colorDiff - offset)); if (currentColor < endColor) { currentColor = endColor; } } else { currentColor = (int) (startColor + (fraction * colorDiff - offset)); if (currentColor > endColor) { currentColor = endColor; } } return currentColor; } /** * 将10进制颜色值转换成16进制。 */ private String getHexString(int value) { String hexString = Integer.toHexString(value); if (hexString.length() == 1) { hexString = "0" + hexString; } return hexString; }}
首先在 evaluate() 方法当中获取到颜色的初始值和结束值,并通过字符串截取的方式将颜色分为RGB三个部分,并将RGB的值转换成十进制数字,那么每个颜色的取值范围就是0~255。
接下来计算一下初始颜色值到结束颜色值之间的差值,这个差值很重要,决定着颜色变化的快慢。在相同的时间内,如果初始颜色值和结束颜色值很相近,那么颜色变化就会比较缓慢,而如果颜色值相差很大,比如说从黑到白,那么就要经历255*3这个幅度的颜色过度,变化就会非常快。
控制颜色变化的速度是通过getCurrentColor()这个方法来实现的,这个方法会根据当前的fraction值来计算目前应该过渡到什么颜色,并且这里会根据初始和结束的颜色差值来控制变化速度,最终将计算出的颜色进行返回。
最后,由于我们计算出的颜色是十进制数字,这里还需要调用一下getHexString()方法把它们转换成十六进制字符串,再将RGB颜色拼装起来之后作为最终的结果返回。
完成了自定义的TypeEvaluator,接下来当然是调用啦,这里我们将它和上面实现的 PointEvaluator 结合起来播放。
imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), new Point(0,0)); ValueAnimator animator1 = ValueAnimator.ofObject(new ColorEvaluator(), "#0000FF", "#FF0000"); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setDuration(3000); animatorSet.play(animator).with(animator1); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Point point = (Point) animation.getAnimatedValue(); v.setX(point.getX()); v.setY(point.getY()); } }); animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { String str = (String) animation.getAnimatedValue(); GradientDrawable drawable = (GradientDrawable) imageView.getBackground(); drawable.setColor(Color.parseColor(str)); } }); animatorSet.start(); }});
这里要注意的是,我给 ImageView 的背景设置的是ShapeDrawable,所以可以用 GradientDrawable去修改填充的颜色,这里用Color类的parseColor() 方法将带“#”的字符串转换成 int,所以在 ColorEvaluator 里生成字符串的时候“#”是不能少的。
这里因为要对View的color进行改变,所以我们也可以使用 ObjectAnimator 来实现这一动画。
了解 ObjectAnimator 的朋友应该都知道,ObjectAnimator内部的工作机制是通过寻找特定属性的get和set方法,然后通过方法不断地对值进行改变,从而实现动画效果。
因此我们就需要在我们的View中定义一个color属性,并提供它的get和set方法。这里就只提一下,就不做介绍啦。
到此,TypeEvaluator的介绍就结束啦。
结束语:本文仅用来学习记录,参考查阅。
- Android属性动画--TypeEvaluator
- 属性动画-TypeEvaluator
- Android 属性动画 多动画执行 与 估值器 TypeEvaluator
- Android开发 之 属性动画(自定义ValueAnimator的TypeEvaluator)
- Android-Property 动画自定义TypeEvaluator
- Android 动画-Interpolator和TypeEvaluator
- 属性动画04TypeEvaluator的使用
- Android属性动画(三)——TypeEvaluator(估值器)和Interpolator(插值器)
- Android 属性动画探究(二)——TypeEvaluator解析与自定义
- Android属性动画(二) ValueAnimator的实际应用 & 自定义TypeEvaluator
- 浅析 Android 动画:自定义 Interpolator 与 TypeEvaluator
- 属性动画高级用法之TypeEvaluator和Interpolator
- Android动画TimeInterpolator(插值器)和TypeEvaluator(估值器)分析
- android动画之interpolator和typeEvaluator用法详解
- Android动画TimeInterpolator(插值器)和TypeEvaluator(估值器)分析
- 属性动画资料文件如何编写?property-animation资源文件 属性动画如何自定义TypeEvaluator
- 逐帧动画 补间动画 属性动画ObjectAnimator ValueAnimator AnimatorSet)演示 TypeEvaluator
- 属性动画Animator玩法/自定义估值器TypeEvaluator实现抛物线曲线动画
- canvas和svg
- 【转】Java线程:新特征-锁(1)
- OBS源码分析--视频采集显示
- 七牛云上传
- struts2+spring4.0+hibernate4三大框架的结合,关于struts.xml的存放位置问题
- Android属性动画--TypeEvaluator
- ant design (antd) FormItem getFieldDecorator Checkbox 无法选中的解决方案
- 删除文件或文件夹的方法
- 从二维点集重建平面形状-浅议凹包算法
- 搜索--17
- caffe 有关prototxt文件的设置解读
- 软件项目行为模式总结
- 域名解析缓存
- 【转】Java线程:新特征-锁(2)