Android中动画的实现方式有三种:属性动画、补间动画、逐帧动画。其中属性动画是指在一定的时间间隔内,通过对给予对象的属性值的改变而达到动画效果,相比于其他两种动画方式,其灵活性要大很多。而补间动画的实现,通常是局限于view对象,而且并不能改变view的属性,只是通过改变视觉效果来达到动画效果,这种局限使得补间动画的效果单一,无法跟属性动画相媲美。所以学好属性动画,对我们及其重要。
属性动画有两个非常重要的类:ValueAnimator类和ObjectAnimator类,其中后者继承于前者,属性动画的实现基本也是靠这两个类,下面我们分别来介绍这两个类是怎么来实现动画效果的。
1.ValueAnimator类
ValueAnimator类是属性动画中最核心的一个类,其工作原理是:通过不断控制值的变化,然后根据值的变化不断地去改变对象的属性,从而达到动画效果。其主要方法有,ValueAnimator.ofInt(itn value)、ValueAnimator.onFloat(float value)、ValueAnimator.ofObject(object value)三种。下面我们先来介绍ofFloat方法。
1.1 ValueAnimator.ofFloat(float value)
具体使用方式,可以用java代码设置,也可用xml文件设置,下面我们来看看代码设置方式是如何使用的:
ValueAnimator animator = ValueAnimator.ofFloat(0, 10);
//设置动画时长
animator.setDuration(300);
//设置动画执行延迟时间
animator.setStartDelay(100);
//设置动画重复次数
animator.setRepeatCount(0);
//设置动画重复模式
animator.setRepeatMode(ValueAnimator.RESTART);
//设置动画监听器,值的改变都会回调
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = animation.getAnimatedValue();
LogUtils.d("当前动画值=" + currentValue);
//改变view的属性值
view.setProperty(currentValue);
view.requestLayout();
}
});
//开始动画
animator.start();
上面我们简单地展示了ValueAnimator的使用,我们在动画监听器那里将动画值的变化打印出来,如下:
从上面可以看到,动画值是在300毫秒内从0增加到10的,然后在这里值的变化过程中,我们不断地对view的属性进行改变,就可以让view产生动画效果了。
1.2 ValueAnimator.ofObject(object value)的使用
这个方法跟上面的ofFloat有所不同,上面改变的对象直接是float值,而这个是针对Object对象,是针对所有的对象的,所以属性动画的灵活性就在这里了,而这object对象是需要开发者自己定义的。下面我们通过一个实例去解析如何使用ofObject方法。
我们属性定义一个Object类为Point,代表一个坐标,如下
public class Point {
float x;
float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
}
我们先来讲解一下估值器(TypeEvaluator)和插值器(Interpolator),插值器是用来反映动画变化规律的, 估值器是基于插值器的那个变化规律,根据自己的需求计算得出返回的数据值。下面我们来实现一个自定义估值器:
public class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
float x = startValue.getX() + (endValue.getX() - startValue.getX()) * fraction;
float y = startValue.getY() + (endValue.getY() - startValue.getY()) * fraction;
Point point = new Point(x, y);
return point;
}
}
下面我们来实现一个需求,将一个button从手机屏幕的左上角通过属性动画移动到屏幕的右下角,这个button的初始位置为屏幕的左上角。看看代码
public void ofObjectBtn(Context context, final Button button) {
//起初点
Point startPoint = new Point(0, 0);
//终点
Point endPoint = new Point(Utils.getScreenWidth(context)-220, Utils.getScreenHeight(context)-200);
ValueAnimator animator=ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
animator.setDuration(5000);
animator.setStartDelay(300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Point currentPoint= (Point) animation.getAnimatedValue();
LogUtils.d("变化的值="+currentPoint.getX()+"///"+currentPoint.getY());
FrameLayout.LayoutParams layoutParams= new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
//改变btn的属性
layoutParams.topMargin= (int) currentPoint.getY();
layoutParams.leftMargin= (int) currentPoint.getX();
button.setLayoutParams(layoutParams);
button.requestLayout();
}
});
animator.start();
}
上面在监听器中,我们用getAnimatedValue方法返回是是Point对象,因为我们传入的values就是Point对象,而且这个返回的point就是我们在估值器PointEvaluator 中计算得到的对象。可以看出,ofObject本质上还是对值的操作, 只是将这些操作封装在一个对象中。效果图如下
2.ObjectAnimator的使用
1.1 ObjectAnimator是在ValueAnimator的基础对动画操作做了进一层的封装,它与ValueAnimator的最大区别是,ValueAnimator是手动赋值给对象属性而实现动画,ObjectAnimator是直接赋值给对象属性去实现动画,所以说ObjectAnimator更加方便、智能。
2.1 ObjectAnimator的基本使用如下示例:
//创建一个ObjectAnimator
ObjectAnimator animator = ObjectAnimator.ofFloat(targetView, propertyName, values ...);
//设置插值器
animator.setInterpolator(interpolator);
//设置估值器
animator.setEvaluator(evaluator);
//设置时间间隔
animator.setDuration(values);
//开始动画
animator.start();
ObjectAnimator实现动画效果的本质是:通过不断控制值的变化,再不断赋值给对象的属性,从而实现动画。上面ofFloat方法中,第一个参数是动画的目标view,第二个是view的属性名字,values是指。我们来看看view的属性名称有哪些:
属性 | 作用 | 数值类型 |
---|---|---|
Alpha | 控制View的透明度 | float |
TranslationX | 控制X方向的位移 | float |
TranslationY | 控制Y方向的位移 | float |
ScaleX | 控制X方向的缩放倍数 | float |
ScaleY | 控制Y方向的缩放倍数 | float |
Rotation | 控制以屏幕方向为轴的旋转度数 | float |
RotationX | 控制以X轴为轴的旋转度数 | float |
RotationY | 控制以Y轴为轴的旋转度数 | float |
ObjectAnimator会根据指定的属性名称,比如(TranslationX)去寻找目标对象的对应的get()和set()方法,因为View类中已经帮我实现了上面这些属性名称的get和set方法,所以如果target是view的话,就不用自己去定义了。ObjectAnimator的用法比较简单,我也就不具体举例子了。
3.AnimatorSet 的使用
这是一个组合动画使用的类,我们可以通过这个类将2个以上动画进行不同的组合,用法如下:
AnimatorSet.play(Animator anim) :播放当前动画
AnimatorSet.after(long delay) :将现有动画延迟x毫秒后执行
AnimatorSet.with(Animator anim) :将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim) :将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) : 将现有动画插入到传入的动画之前执行
当然也可以用xml文件定义的形式来设置,anima_set文件如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially" >
// 表示Set集合内的动画按顺序进行
// ordering的属性值:sequentially & together
// sequentially:表示set中的动画,按照先后顺序逐步进行(a 完成之后进行 b )
// together:表示set中的动画,在同一时间同时进行,为默认值
<set android:ordering="together" >
// 下面的动画同时进行
<objectAnimator
android:duration="2000"
android:propertyName="translationX"
android:valueFrom="0"
android:valueTo="300"
android:valueType="floatType" >
</objectAnimator>
<objectAnimator
android:duration="3000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType" >
</objectAnimator>
</set>
<set android:ordering="sequentially" >
// 下面的动画按序进行
<objectAnimator
android:duration="1500"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType" >
</objectAnimator>
<objectAnimator
android:duration="1500"
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" >
</objectAnimator>
</set>
</set>
在java代码中启动动画如下:
//以一个button为例
mButton = (Button) findViewById(R.id.Button);
//加载xml文件定义的动画
AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.set_animation);
//设置目标
animator.setTarget(mButton);
animator.start()
4.ViewPropertyAnimator用法
直接操作动画属性从而实现动画,典型用法为:View.animate.xx(value).xx(value);用代码示例:
button.animate().alpha(0.5f).translationX(100).setDuration(100).start();
button.animate().alpha(0.5f).x(100).y(100);
好了,属性动画的用法就讲到这里了。其实我们着重理解那个估值器和插值器的使用方法,其他的都比较简单。
附上源码地址:石月的GitHub