概述:
Property Animation,属性动画非常强大,Android官方API如是说:属性动画系统是一个强大的框架,它几乎可以将动画添加到任何东西上。它是在View Animation之后引入的,那么相比较有什么优势:
1,Property Animation不仅可以使View收缩,旋转而且可以改变View的color,size等属性值。而且作用范围不局限于View,而是任何对象。
2,Property Animation使视图发生了改变同时View的真实位置也发生了改变。
3,更少的代码做更多的事情。
详解:
了解Property Animation可以从下面三个方面来了解:
1,Animators
Animator相关类为我们提供了创建属性动画的方法。涉及到的类如下图所示:
Animator:为我们提供了一些基础的方法,如添加监听,开始,取消动画等方法。
ValueAnimator:提供了一个简单的计时引擎,通过计算动画值并将它们设置在目标对象上, 用于运行动画。可以用代码定义,也可以用xml,个人觉得在代码中比较常用,这里列举下在代码中创建的方法:
ValueAnimator()
static ValueAnimator ofArgb(int... values)
static ValueAnimator ofFloat(float... values)
static ValueAnimator ofInt(int... values)
static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)
ObjectAnimator: ValueAnimator的这个子类提供了对目标对象上的动画属性的支持。View对象对应的propertyName一般有这几个:alpha, translationX,translationY,x,y,rotation,rotationX,rotationY,scaleX,scaleY。如果记不住可在View的源码中查看。
ObjectAnimator()
static ObjectAnimator ofInt(Object target, String propertyName, int... values)
static ObjectAnimator ofArgb(Object target, String propertyName, int... values)
static ObjectAnimator ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)
...
TimeAnimator: 比ValueAnimator多了一个TimeListener,用于监听执行总时间以及当前帧和上一帧的时差。
//示例
timeAnim.setTimeListener(new TimeAnimator.TimeListener() {
@Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
}
});
AnimatorSet:可以将多个Animator一起或者按照一定的顺序执行。
//示例
AnimatorSet anim = new AnimatorSet();
anim.play(rotateAnim).after(transferAnim).with(timeAnim);
anim.start();
2,Evaluators
赋值器告诉属性动画系统如何计算给定属性的值。Android提供了下面这些赋值器。
//这是Android中IntEvaluator实现代码我们也可以通过继承TypeEvaluator来实现自己的Evaluator。关键重载
evaluate方法就可以了。
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
ValueAnimator提供了setEvaluator(TypeEvaluator value)来设置赋值器。那计算出来的值我们也可以在下面的回调中通过来getAnimatedValue()获取。
transferAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
valueAnimator.getAnimatedValue();
}
});
2,Interpolators
时间插值器定义动画中的具体值如何作为时间的函数计算。通俗讲就是用什么样的速率(匀速,变速等待)来展示。Android提供了下面这些插值器,基本上见名知意。
//这是Android中AccelerateInterpolator 实现代码,我们也可以通过继承Interpolator 来实现自己的Interpolator ,关键重载getInterpolation(float input)方法就可以了。
public class AccelerateInterpolator implements Interpolator {
private final float mFactor;
private final double mDoubleFactor;
public AccelerateInterpolator() {
mFactor = 1.0f;
mDoubleFactor = 2.0;
}
public AccelerateInterpolator(float factor) {
mFactor = factor;
mDoubleFactor = 2 * mFactor;
}
public AccelerateInterpolator(Context context, AttributeSet attrs) {
TypedArray a =
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AccelerateInterpolator);
mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f);
mDoubleFactor = 2 * mFactor;
a.recycle();
}
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
}
ValueAnimator提供了setInterpolator和getInterpolator方法来设置插值器。
示例:
通过上面的文字,我们了解了创建属性动画的基本类和方法,我们通过示例来进一步熟悉用法。
猎豹清理大师通知栏清理界面有一个闪星星的动画效果,我们实现下后效果如下图:
实现代码
首先新建一个ShiningStar.java的类。
public class ShiningStar extends View {
/**
* 星星的坐标及大小
*/
private static final int[][] starPosition = new int[][] {
{80, 80, 66},{160, 80, 80},{240,160, 100},{120, 240, 120},{360, 480, 66}, {600, 600, 120}, {720, 500, 120},
{360, 100, 66}, {600, 160, 120}, {720, 240, 120},{860, 80, 80}
};
/**
* 星星存储器
*/
private List<Star> stars = new ArrayList<Star>();
/**
* 星星资源
*/
private Bitmap bitmap = null;
/**
* 画笔
*/
private Paint paint = null;
public ShiningStar(Context context) {
super(context);
init();
}
public ShiningStar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ShiningStar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap != null) {
for (int i = 0; i < stars.size(); i++) {
canvas.save();//这样使每一个星星的状态独立
Rect dst = new Rect(stars.get(i).x, stars.get(i).y, stars.get(i).x + stars.get(i).size, stars.get(i).y + stars.get(i).size);
canvas.scale(stars.get(i).scale, stars.get(i).scale, stars.get(i).x + stars.get(i).size/2, stars.get(i).y + stars.get(i).size/2);
paint.setAlpha((int)stars.get(i).alpha);
canvas.drawBitmap(bitmap, null, dst, paint);
canvas.restore();
}
}
}
/**
* 初始化
*/
private void init() {
initStars();
initAnimation();
}
/**
* 初始化星星对象
*/
private void initStars() {
for (int i = 0; i < starPosition.length; i++) {
final Star star = new Star();
star.x = starPosition[i][0];
star.y = starPosition[i][1];
star.size = starPosition[i][2];
star.scale = 1;
star.alpha = 255;
stars.add(star);
}
}
/**
* 初始化动画及绘制元素的对象
*/
private void initAnimation() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.star_icon);
paint = new Paint();
paint.setAlpha(255);
ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 255, 0);
scaleAnim.setInterpolator(new LinearInterpolator());
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
scaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
boolean flag = false;
for (int i = 0; i < stars.size(); i++) {
if (flag) {
stars.get(i).scale = ((float)animation.getAnimatedValue())/255;
stars.get(i).alpha = (float)animation.getAnimatedValue();
} else {
stars.get(i).scale = 1 - ((float)animation.getAnimatedValue())/255;
stars.get(i).alpha = 255 - (float)animation.getAnimatedValue();
}
flag = !flag;
}
postInvalidate();
}
});
scaleAnim.start();
}
/**
* 星星属性
*/
class Star {
int x;
int y;
int size;
float scale;
float alpha;
}
}
2,新建一个layout文件。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="#2196F3"
tools:context="com.wayne.android.viewanimation.MainActivity">
<com.wayne.android.viewanimation.ShiningStar
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
3,在Activity中引用
setContentView(R.layout.activity_main);
这个例子中我们用到了ValueAnimator 来实现星星的闪动效果。那我们能否改造一下使用ObjectAnimator呢,当然可以。
public class ShiningStar extends View {
/**
* 星星的坐标及大小
*/
private static final int[][] starPosition = new int[][] {
{80, 80, 66},{160, 80, 80},{240,160, 100},{120, 240, 120},{360, 480, 66}, {600, 600, 120}, {720, 500, 120},
{360, 100, 66}, {600, 160, 120}, {720, 240, 120},{860, 80, 80}
};
/**
* 星星存储器
*/
private List<Star> stars = new ArrayList<Star>();
/**
* 星星资源
*/
private Bitmap bitmap = null;
/**
* 画笔
*/
private Paint paint = null;
protected float starScale = 1f;
protected float starAlpha = 255f;
public ShiningStar(Context context) {
super(context);
init();
}
public ShiningStar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ShiningStar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmap != null) {
boolean flag = false;
for (int i = 0; i < stars.size(); i++) {
canvas.save();//这样使每一个星星的状态独立
Rect dst = new Rect(stars.get(i).x, stars.get(i).y, stars.get(i).x + stars.get(i).size, stars.get(i).y + stars.get(i).size);
android.util.Log.i("TestShining", "scale: " + starScale);
if (flag) {
canvas.scale(starScale, starScale, stars.get(i).x + stars.get(i).size/2, stars.get(i).y + stars.get(i).size/2);
paint.setAlpha((int)starAlpha);
} else {
canvas.scale(1 - starScale, 1 - starScale, stars.get(i).x + stars.get(i).size/2, stars.get(i).y + stars.get(i).size/2);
paint.setAlpha((int)(255 - starAlpha));
}
flag = !flag;
canvas.drawBitmap(bitmap, null, dst, paint);
canvas.restore();
}
}
}
/**
* 初始化
*/
private void init() {
initStars();
initAnimation();
}
/**
* 初始化星星对象
*/
private void initStars() {
for (int i = 0; i < starPosition.length; i++) {
final Star star = new Star();
star.x = starPosition[i][0];
star.y = starPosition[i][1];
star.size = starPosition[i][2];
stars.add(star);
}
}
public float getStarScale() {
return starScale;
}
public void setStarScale(float starScale) {
this.starScale = starScale;
postInvalidate();
}
public float getStarAlpha() {
return starAlpha;
}
public void setStarAlpha(float starAlpha) {
this.starAlpha = starAlpha;
postInvalidate();
}
/**
* 初始化动画及绘制元素的对象
*/
private void initAnimation() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.star_icon);
paint = new Paint();
paint.setAlpha(255);
ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(this, "starScale", 0, 1, 0);
scaleAnim.setInterpolator(new LinearInterpolator());
scaleAnim.setDuration(1000);
scaleAnim.setRepeatCount(-1);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, "starAlpha", 0, 255, 0);
alphaAnim.setInterpolator(new LinearInterpolator());
alphaAnim.setDuration(1000);
alphaAnim.setRepeatCount(-1);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(scaleAnim, alphaAnim);
animatorSet.playTogether(scaleAnim);
animatorSet.start();
}
/**
* 星星属性
*/
class Star {
int x;
int y;
int size;
}
}
从上面代码中我们要注意,在ShiningStar 类中增加了两个属性starScale 和starAlpha 并且增加了setter和getter方法,在set中View面进行刷新。这样ObjectAnimator才可以对这两个属性进行操作并且传递给界面。
结语:
Property Animation比较常用,本片主要总结了下知识点,示例并不全面。后面文章会有更多应用。