Android中动画之四属性动画高级用法二
一.概况
目的
在上一篇中记录了“对任何类的任何属性进行动画”基础,那只是简单的对属性值(int,float类型)进行动画处理。下面我们对非int,float类型的属性值进行处理。
涉及的知识点
- 自定义估值器
- ofObjec和ofArgb方法介绍使用
- ValueAnimator的高级用法
- ObjectAnimator的高级用法
二.自定义估值器
2.1先看一个系统自带的估值器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);
}
}
自定义估值器只要重写evaluate方法就好。
从上面的FloatEvaluator的实现我们可知实现自定义估值器刚需就是重写evaluate方法。下面分析一下其各个参数含义。
分析重写方法中的三个参数
- fraction:由插值器获得的动画的完成度
- startValue:动画的开始值
- endValue:动画的结束值
三.View背景颜色动画
目的
通过View背景颜色动画来熟悉一下ofArgb和ofObject方法。
注意
- ValueAnimator和ObjectAnimator都有ofArgb方法,其是用来实现颜色变化的只不过它引入的比较晚API21以后才引入。
- 通过ValueAnimator和ObjectAnimator的ofObject也可以实现背景颜色动画,且兼容性比上面ofArgb方法好。
先看效果
实现代码
布局文件很简单这里就不贴了,只是包含一个TextView
四种实现方法
第一种:
使用ObjectAnimator.ofArgb,
private void startAni() {
//API21以上能用
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ObjectAnimator ani = ObjectAnimator.ofArgb(showTv,"backgroundColor",0xffff0000,0xff00ff00,0xff0000ff);
ani.setDuration(4000);
ani.start();
}
}
比较好理解ofArgb前俩个参数一个目标对象,一个属性名称;后面是32位的int值用来表示颜色值。
第二种:
使用ValueAnimator.ofArgb并设置监听器,自己更改背景颜色。
private void startAni() {
//API21以上能用
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
ValueAnimator ani = ValueAnimator.ofArgb(0xffff0000,0xff00ff00,0xff0000ff);
ani.setDuration(4000);
ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
showTv.setBackgroundColor((Integer) animation.getAnimatedValue());
}
});
ani.start();
}
}
第三种
使用系统的ArgbEvaluator估值器,这样能提供较好兼容性
private void startAni() {
ObjectAnimator ani = ObjectAnimator.ofObject(showTv,"backgroundColor",new ArgbEvaluator(),0xffff0000,0xff00ff00,0xff0000ff);
ani.setDuration(4000);
ani.start();
}
第四种
使用系统的ArgbEvaluator估值器,这样能提供较好兼容性
private void startAni() {
ValueAnimator ani = ValueAnimator.ofObject(new ArgbEvaluator(),0xffff0000,0xff00ff00,0xff0000ff);
ani.setDuration(4000);
ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
showTv.setBackgroundColor((Integer) animation.getAnimatedValue());
}
});
ani.start();
}
分析
上面的第三种和第四种方法相对第一种,第二种方法提供了较好的兼容性。
四.ValueAnimator的高级用法
4.1背景
通过上一篇Android中动画之三属性动画高级用法一,我们知道对任意类(Object)的任意属性(abc)做动画,想要成功要具备俩个条件如下:
1.Object必须提供setAbc方法;如果动画的时候没有传递初始化值,那么还要有getAbc方法,因为系统要去取abc属性的初始值(如果不满足这个条件,直接crash);
2.Object的setAbc对属性abc所做的改变必须通过某种方法表现出来,最常见的就是UI的变化。(如果不满足这个条件,不会crash,但是没有效果)
我们想要实验一下ValueAnimator的ofObject方法,想要直观的看到动画效果,那我们就要一个使用场景。
4.2场景
在一个自定义View中实现一个圆完成平移动画。可能看起来很简单,但是关键是理解其中的原理。
4.3分析
先看一下ValueAnimator的ofObject方法需要什么
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
从上面可知想要对任意类的任意属性做动画,那么我们需要自己的估值器TypeEvaluator,类的属性(这里可以是基本类型如int,float也可以是任何的类)数组;
4.4解决方案
自定义一个View,在里面连续画圆,也就相当于对View的位置类属性进行了动画处理。
4.5效果和具体代码
效果如下图
具体代码
圆Pointer(圆就是一个大的点)如下:
主要是坐标属性
public class Pointer {
private float x;
private float y;
public Pointer(float x,float y){
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
自定义估值器
坐标各自求值,并取得变化后的Pointer对象。
public class PointerEvaluator implements TypeEvaluator<Pointer>{
@Override
public Pointer evaluate(float fraction, Pointer startValue, Pointer endValue) {
float x = startValue.getX() + fraction*(endValue.getX() - startValue.getX());
float y = startValue.getY() + fraction*(endValue.getY() - startValue.getY());
return new Pointer(x,y);
}
}
自定义View
public class AnimatorView extends View{
private static final float RADIUS = 50;
private Paint mPaint;
private Pointer curPointer;
public AnimatorView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//初始化画笔
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
}
@Override
protected void onDraw(Canvas canvas) {
if (curPointer == null){
//最初
//初始化圆(位置主要是)
curPointer = new Pointer(RADIUS,RADIUS);
//画出来
drawCicle(canvas);
//开始动画
startAni();
}else {
drawCicle(canvas);
}
}
private void startAni() {
Pointer startP = new Pointer(RADIUS,RADIUS);
Pointer endP = new Pointer(getWidth()-RADIUS,getHeight()-RADIUS);
final ValueAnimator ani = ValueAnimator.ofObject(new PointerEvaluator(),startP,endP);
ani.setDuration(4000);
ani.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
curPointer = (Pointer) animation.getAnimatedValue();
//关键:获取新位置后调用invalidate,执行画圆操作。
invalidate();
}
});
ani.start();
}
private void drawCicle(Canvas canvas) {
canvas.drawCircle(curPointer.getX(),curPointer.getY(),RADIUS,mPaint);
}
}
上面的AnimatorView中流程如下:
- 构造方法中初始化画笔
- onDraw中判断curPointer是否为空,为空刚进入绘制过程。初始化Pointer;开始画圆;开始动画。
- 在动画的回调监听中获取现在圆的位置,调用invalidate重新执行onDraw,周而复始随之动画的进行不断改变圆的位置,达到对圆添加动画的目的。
五.ObjectAnimator的高级用法
实现效果
自定义View中圆平移并且颜色发生变化,这里实现的效果比较简单,用现有的属性动画组合可以实现,但是重点是理解ObjectAnimator的ofObject方法以及自定义估值器。
看起来简单,实现起来还是有些要注意的地方的
Activity中对自定义View不做处理,xml中添加自定义View设置宽高为填充满就好。下面几个是关键的类。
自定义View
public class ArgbAndPosView extends View{
//圆的半径
private static final int RADIUS = 50;
//画笔
private Paint mPaint;
//自定义View的属性值
private Pointer pointer;
//动画属性的get方法
public Pointer getPointer() {
return pointer;
}
//动画属性的set方法
public void setPointer(Pointer pointer) {
this.pointer = pointer;
//设置颜色值
mPaint.setColor(pointer.getColor());
//请求刷新
invalidate();
}
public ArgbAndPosView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//构造方法中初始化画笔
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
}
@Override
protected void onDraw(Canvas canvas) {
if (pointer == null){
//初次进入,初始化将被进行动画处理的属性值
pointer = new Pointer(RADIUS,RADIUS,Color.RED);
//画出来
drawCircle(canvas);
//开始动画
startAni();
}else {
//每次刷新都重画
drawCircle(canvas);
}
}
private void startAni() {
//起点属性值
Pointer startP = new Pointer(RADIUS,RADIUS,0xffff0000);
//终点属性值
Pointer endP = new Pointer(getWidth()-RADIUS,getHeight()-RADIUS,0xff00ff00);
//对本自定义View的pointer属性进行动画处理,动画时间为4000毫秒,从startP到endP,插值器用的默认的
ObjectAnimator ani = ObjectAnimator.ofObject(this,"pointer",new PointerEvaluator(),startP, endP);
ani.setDuration(4000);
ani.start();
}
//画圆
private void drawCircle(Canvas canvas) {
canvas.drawCircle(pointer.getX(),pointer.getY(),RADIUS,mPaint);
}
}
自定义View中的属性值Pointer
自定义View中将被进行动画处理的属性,这里是个Pointer类。
public class Pointer {
private float x;
private float y;
private int color;
public Pointer(float x,float y,int color){
this.x = x;
this.y = y;
this.color = color;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public int getColor() {
return color;
}
}
关键,自定义估值器
估值器很重要,分别对Pointer中的属性进行计算处理,获取现在时间点对应的动画属性值。
public class PointerEvaluator implements TypeEvaluator<Pointer>{
@Override
public Pointer evaluate(float fraction, Pointer startValue, Pointer endValue) {
//处理坐标
float x = startValue.getX() + fraction*(endValue.getX() - startValue.getX());
float y = startValue.getY() + fraction*(endValue.getY() - startValue.getY());
//处理颜色,处理过程和ArgbEvaluator中一样
int startInt = startValue.getColor();
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ( startInt & 0xff) / 255.0f;
int endInt = endValue.getColor();
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ( endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
int color = Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
return new Pointer(x,y,color);
}
}
其实上面的Pointer中还可以添加更多的属性以控制更多的动画效果。
五.总结
通过本文可以掌握:
- 自定义估值器
- ofObjec和ofArgb方法介绍使用
- ValueAnimator的高级用法
- ObjectAnimator的高级用法
后两个主要是ofObject方法的用法,也就是对任意类的任意属性进行动画操作。
上一篇: 浅谈Redis五个对象类型的底层原理
下一篇: python爬爬有道词典