Android动画的实现方式有三种:属性动画、补间动画、逐帧动画。其中属性动画是指在一定的时间间隔内,通过改变目标对象的属性值而达到动画效果,相比于其他两种动画方式,灵活性大。而补间动画,通常是局限于View对象,而且不能改变View的属性,只是通过改变视觉效果来达到动画效果,这种局限使得补间动画的效果单一,无法跟属性动画相媲美。所以说学好属性动画非常重要
属性动画有两个非常重要的类:ValueAnimator类和ObjectAnimator类,后者继承于前者,属性动画的实现基本也是靠这两个类
一.ValueAnimator类
ValueAnimator类是属性动画中最核心的一个类,其工作原理是:通过控制值的不断变化,然后根据值的变化不断地去改变目标的属性,最终达到动画效果。主要方法有:ValueAnimator.ofInt(int... values)、ValueAnimator.onFloat(float... values)、ValueAnimator.ofObject(TypeEvaluator evaluator, Object... values)三种。下面来介绍一下ofFloat方法
1.1 ValueAnimator.ofFloat(float... values)
ofFloat方法返回一个ValueAnimator对象,可以接受多个float类型的参数,看看它的简单使用方式
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
view.requestLayout();
}
});
//开始动画
animator.start();
在动画监听器中将变化的属性值打印出来,如下:
可以看到,动画值是在300毫秒内从0增加到10的,然后在变化过程中,通过不断地改变view的属性并渲染View,从而呈现出动画效果,另外ofInt方法的使用方式也是一样的,只是参数类型不同而已
1.2 ValueAnimator.ofObject(TypeEvaluator evaluator, Object... values)的使用
这个方法跟上面的ofFloat有所不同,上面改变的对象直接是float值,而这个改变的是一个Object对象,所以属性动画的灵活性就在这里了,这个object对象需要开发者自己定义。下面通过一个实例去解析一下如何使用ofObject方法
定义一个类Point作为Object参数,它代表的是一个坐标,如下
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),插值器是用来反映动画变化规律的,比如线性变化,先变大后变小等等,估值器是基于插值器的变化规律,不断地改变自定义的那个Object的相关值,说到底动画就是体现某些不断变化的值,所以说变化是动画的灵魂,下面来实现一个自定义的估值器:
public class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
//通过返回的动画速率fraction,去改变Object的值
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实例,第一个参数是自定义的估值器
ValueAnimator animator=ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
animator.setDuration(5000);
animator.setStartDelay(300);
//监听value的变化
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获得自定义估值器中evaluate方法返回的Point对象,代表当前的变化值
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();
}
在上面onAnimationUpdate中,通过animation.getAnimatedValue()返回的point就是我们在估值器PointEvaluator的evaluate方法计算得到的那个对象,通过这个变化的Point对象去改变x值和y值,ofObject本质上还是对值的操作, 只是将这些操作都封装在对象里面。效果图如下
二.ObjectAnimator的使用
ObjectAnimator是在ValueAnimator的基础对动画操作做了封装,它与ValueAnimator的最大区别是,ValueAnimator需要自己手动改变对象的属性,而ObjectAnimator直接设置对象的属性和其他参数即可实现动画,所以ObjectAnimator更方便
ObjectAnimator的基本使用示例:
//创建一个ObjectAnimator
ObjectAnimator animator = ObjectAnimator.ofFloat(targetView, propertyName, values ...);
//设置插值器
animator.setInterpolator(interpolator);
//设置估值器
animator.setEvaluator(evaluator);
//设置时间间隔
animator.setDuration(values);
//开始动画
animator.start();
ObjectAnimator其实它里面还是用ObjectAnimator来实现的,在它的ofFloat方法中,第一个参数是动画的目标view,第二个参数是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 |
三.AnimatorSet 的使用
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" >
// 动画执行顺序的属性值: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代码中使用xml定义的动画,如下
//以一个button为例
mButton = (Button) findViewById(R.id.Button);
//加载xml文件定义的动画
AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.set_animation);
//设置目标
animator.setTarget(mButton);
animator.start()
四.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