《Android开发艺术探索》笔记(七)——Android动画深入分析

《Android开发艺术探索》笔记(七)——Android动画深入分析

作者:cmad 时间:2016-05-18 分类:Android 评论:0条 浏览:423



  Android的动画可以分为三种:View动画、帧动画和属性动画。

  View动画通过对场景里的对象不断做图像变换从而产生动画效果,View动画针对的对象是View,它支持4种动画效果,分别是平移动画、缩放动画、旋转动画和透明动画,View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation和AlphaAnimation。

  帧动画是顺序播放一组预先定义好的图片,类似于电影播放(我觉得更像gif)。系统提供了AnimationDrawable来使用帧动画。首先需要在xml里定义:

<animation-list>
<item android:drawable="@drawable/image1" android:duration="100"/>
<item android:drawable="@drawable/image2" android:duration="100"/>
<item android:drawable="@drawable/image3" android:duration="100"/>
</animation-list>

  然后将上述的Drawable作为View的背景并通过Drawable来播放动画:

ImageView imageView = (ImageView) findViewById(R.id.image);
imageView.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable) imageView.getBackground();
drawable.start();

  帧动画使用比较简单,但比较容易引起OOM,所以使用帧动画时应尽量避免使用过多尺寸较大的图。

  View动画和帧动画我们就介绍这么多,下面主要介绍一下属性动画。

属性动画

  属性动画是API 11新加入的特性,和View动画不同,它对作用对象进行了扩展,属性动画可以对任何对象做动画,甚至还可以没有对象。除了作用对象进行了扩展以外,属性动画的效果也得到了加强,不再像View动画那样只支持四种简单的变换。属性动画中有ValueAnimator、ObjectAnimator和AnimatorSet等概念,通过它们可以实现绚丽的动画。

  因为属性动画是API 11才加入的,如果低版本要使用属性动画可以使用兼容库nineoldandroids这个库,但是需要注意的是如果低版本使用这个库看起来是使用属性动画,但其实内部是用的View动画来实现的。高于API 11则是属性动画,使用方法跟Android自带 的属性动画使用一致。

  下面来看几个简单的例子:

ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).setDuration(500).start();

  上面的代码很简单,第一个参数myObject是一个View是我们针对动画的对象;第二个参数是一个字符串,translationY是view Y轴的偏移量,是我们动画针对的属性;第三个参数是针对第二个参数要平移的值。上面代码是将myObject在500ms里沿着Y轴向上平移一个高度的距离。

ValueAnimator colorAnim = ObjectAnimator.ofInt(myObject,"backgroundColor", 0xFFFF8080, 0xFF8080FF);
colorAnim.setDuration(1000);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatCount(ValueAnimator.REVERSE);
colorAnim.start();

  上面的代码实现了针对View的backgroundColor属性做了颜色渐变的动画,其中setEvaluator设置了ArgbEvaluator的估值器,以及setRepeatCount和setRepeatCount设置重复播放此处和重复模式。

AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(myObject, "rotationX", 0, 360),
ObjectAnimator.ofFloat(myObject, "translationY", 0, 90),
ObjectAnimator.ofFloat(myObject, "scaleX", 1, 1.5f),
ObjectAnimator.ofFloat(myObject, "alpha", 1, 0.25f, 1)
);
set.setDuration(3000).start();

  上面代码用了AnimatorSet集合实现了同时播放一系列动画。

插值器和估值器

  TimeInterpolator插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比,系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)、DecelerateInterpolator(减速插值器:动画越来越慢)等。

  TypeEvaluator估值器,它的作用是根据当前属性改变的百分比来计算改变的属性的值,系统预置的有IntEvaluator(针对整型属性)、FloatEvaluator(针对浮点型属性)和ArgbEvaluator(针对Color属性)。

  属性动画的插值器和估值器很重要,他们是实现属性动画的重要组成,也是相互配合使用的。

  动画默认刷新频率是10ms/帧,比如我们现在一个动画的时间是40ms,那么动画开始后20ms即时间过去百分比为0.5,如果使用LinearInterpolator插值器,那么这个时候计算的属性百分比也是0.5,。我们来看一下LinearInterpolator的源码:

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

public LinearInterpolator() {
}

public LinearInterpolator(Context context, AttributeSet attrs) {
}

public float getInterpolation(float input) {
return input;
}

/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
}

  getInterpolation方法输入的时间流逝百分比,返回的是属性改变百分比,很明显这里是直接返回的时间流逝的百分比,即上面我们提到的时间流逝0.5那么属性改变百分比也为0.5,这就是线性插值器,也就实现了动画的匀速播放。

  再看一下IntEvaluator估值器的源码:

public class IntEvaluator implements TypeEvaluator<Integer> {

public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}

  evaluate方法参数第一个是由插值器计算出来的属性改变百分比,第二个和第三个参数是属性值改变的开始值和结束值。算法很简单,就是返回起始值加上结束值与起始值的插值乘以属性改变百分比。

  这就是插值器和估值器的工作原理,知道了他们的工作原理后我们可以实现自定义的插值器和估值器,实现我们的自定义动画效果。

属性动画工作原理

  属性动画是不是可以作用在任意属性上呢?

  属性动画原理其实是根据插值器和估值器获得的值通过反射去改变对应对象对应属性的值,即通过反射调用对象属性的set方法去设置对应值。那么就要求该对象必须提供该属性的set和get(可选)方法,而如果要界面上看到动画效果,则要求对该属性的改变能通过某种方式反映到界面上表现出来。总结如下:

  • 对象必须提供属性的set方法,如果动画的时候没有传递初始值,那么还要提供属性的get方法,因为系统通过get方法去获得属性的初始值。
  • 对象的set方法对属性的改变必须能够通过某种方法反映出来,比如带来UI的改变之类的,否则看不到动画效果。

那如果对象没有提供set、get方法呢怎么办?是不是就能给他设置属性动画?是的,如果对象的该属性没有set、get方法是不能对该属性做属性动画,那我们怎么办呢?提供如下几种办法:

1.给你的对象添加set和get方法,如果你有权限的话

  很简单,既然没有set、get方法不行,那我们就添加set、get方法嘛。但是前提是你有权限,比如你自己写的类或者你能修改这个类的源码。那如果不能修改呢?比如系统的类。看下面的方法。

2.用一个类来包装原始对象,间接为其提供get和set方法

  即我们自己再写一个封装内,提供get、set方法来改变对象的属性值,然后将属性动画作用在我们自己的封装对象上。

  比如我们要实现改变View的宽度,我们知道调用View的setWidth方法并不能改变View的宽度,而是需要获得view的LayoutParams然后设置LayoutParams的width属性才行,这个时候我们就可以这样:

public class ViewWrapper{
private View mTarget;

public ViewWrapper(View target){
this.mTarget = target;
}

public int getWidth(){
return mTarget.getLayoutParams().width;
}

public void setWidth(int width){
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}

这样我们就可以将属性动画作用在ViewWrapper上了。

3.采用ValueAnimator,监听动画过程,自己实现属性的改变

  先介绍一下属性动画的监听:AnimatorListener(动画监听)、AnimatorUpdateListener(动画过程监听)。

public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}

  AnimatorListener 提供了动画开始、结束、取消和重复的监听。

public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}

  AnimatorUpdateListener 提供动画过程监听,动画每刷新一帧调用一次监听器。

  我们可以通过ValueAnimator加上AnimatorUpdateListener 来实现对属性的改变。如下:

private void performAnimate(final View target, final int start, final int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {

// 持有一个IntEvaluator对象,方便下面估值的时候使用
private IntEvaluator mEvaluator = new IntEvaluator();

@Override
public void onAnimationUpdate(ValueAnimator animator) {
// 获得当前动画的进度值,整型,1-100之间
int currentValue = (Integer) animator.getAnimatedValue();
Log.d(TAG, "current value: " + currentValue);

// 获得当前进度占整个动画过程的比例,浮点型,0-1之间
float fraction = animator.getAnimatedFraction();
// 直接调用整型估值器通过比例计算出宽度,然后再设给Button
target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
target.requestLayout();
}
});

valueAnimator.setDuration(5000).start();
}


相关推荐
更多

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>