Android的动画可以分为三种,View动画、帧动画、属性动画。View动画通过对场景里的对象不断做图像变化(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐进式动画,而且View动画支持自定义。帧动画可以理解为图片切换动画(如果图片过多,会导致OOM)。属性动画为API11的新特性,属性动画通过通过动态地改变对象的属性从而达到动画效果。

  • View动画

View动画的种类:View动画的四种变换效果对应着Animation的四个子类。TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation。这四种动画可以通过XML来定义,也可以通过代码动态创建。对于View动画来说,建议XML来定义,这是因为XML格式的动画可读性好。

View动画的四种变换
名称 标签 子类 效果
平移动画 <translate> TranslateAnimation 移动View
缩放动画 <scale> ScaleAnimation 放大或缩小View
旋转动画 <rotate> RotateAnimation 旋转View
透明度动画 <alpha> AlphaAnimation 改变View的透明度
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:shareInterpolator="true"
android:duration="1000"
android:fillAfter="false"> <alpha android:fromAlpha="float"
android:toAlpha="float" />
<scale android:fromXScale="float"
android:toXScale="float"
android:fromYScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float" /> <translate android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float" />
<rotate android:fromDegrees="float"
android:toDegrees="float"
android:pivotX="float"
android:pivotY="float" />
</set>

View动画可以是单个动画,也可以是一系列动画组成。<set>表示动画集合,对应AnimationSet类。

android:interpolator:表示动画集合所使用的插值器,插值器影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放速度。

android:shareInterpolator:表示集合中的动画是否共享同一个插值器。如果不指定插值器,那么子动画就需要单独指定所需要的插值器或者使用默认值。

AlphaAnimation anim = (AlphaAnimation) AnimationUtils.loadAnimation(this, R.anim.anim_test);
TextView tv = findViewById(R.id.tv);
tv.startAnimation(anim);

除了XML定义动画之外,还可以使用代码创建动画。

AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(300);
TextView tv = findViewById(R.id.tv);
tv.startAnimation(alphaAnimation);

注意:通过Animation的setAnimationListener的方法可以给View动画添加过程监听。如适用于带动画的启动页,监听动画完成后进入MainActivity。

anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
});

自定义View动画:

自定义View动画是一件简单而复杂的事情。说简单是:因为派生一种新动画只需要集成Animation这个抽象类,然后重写它的initialize和applyTransformation方法。在initialize中进行初始化工作,在applyTransformation中进行相应的矩阵变换即可。很多时候需要Camera来简化矩阵变换过程。说复杂,是因为需要运用到矩阵变换的过程,使用到数学上的概念。之后再做相关的分析讲解。

  • 帧动画

帧动画顺序播放一组预先定义好的图片,类似电影播放。不同于View动画,系统提供另外一个类AnimationDrawable来使用帧动画。帧动画的使用比较简单,先使用XML来定义一个AnimationDrawable。在drawable目录下创建:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/img1" android:duration="500"/>
<item android:drawable="@drawable/img2" android:duration="500"/>
<item android:drawable="@drawable/img3" android:duration="500"/>
</animation-list>

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

TextView tv = findViewById(R.id.tv);
tv.startAnimation(alphaAnimation);
AnimationDrawable animationDrawable = (AnimationDrawable) tv.getBackground();
animationDrawable.start();

注意帧动画比较容易引起OOM,所以避免使用尺寸较大的图片。

  • View动画的特殊场景使用

在一些特殊场景下,如ViewGroup中控制子元素的出场效果,在Activity中实现不同Activity之间的切换效果。

LayoutAnimation作用于ViewGroup,作为ViewGroup指定一个动画,这样当它的子元素出场时都具有这样的效果。常见的ListView的Item的出场以一定的动画效果出现。

定义LayoutAnimation如下:

//res/anim/anim_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
android:animationOrder="normal"
android:animation="@anim/anim_item" />

delay表示子元素开始动画的时间延迟。比如说元素入场动画周期为300ms,那么0.5表示每个子元素都需要延迟150ms才能播放入场动画。第一个子元素延迟150ms开始播放入场动画,第二个子元素延迟300m开始播放入场,以此类推。

animationOrder:子元素动画顺序,normal, reverse和random。

android:animation: 为元素指定具体的入场动画。

为ViewGroup指定android:layoutAnimation属性,对于ListView而言,指定之后就是Item的出场动画了。除了XML中指定LayoutAnimation之外,还可以通过LayoutAnimationController来实现,具体如下:

ListView listView = findViewById(R.id.list_view);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);

Activity的切换效果:

Activity有默认的切换效果,但是这个效果是可以自定义的,主要用到overridePendingTransition(int enterAnim, int exitAnim)这个方法,这个必须在startActivity(intent)和finish()之后被调用才能生效,它的参数含义如下:

enterAnim - Activity被打开时,所需要的动画资源id

exitAnim - Activity被暂停时,所需要的动画资源id

当启动一个Activity时,可以按照如下方式为其添加自定义的切换效果:

Intent intent = new Intent(this, TestActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim, R.anim.exitAnim);

当Activityt退出时,也可以为其指定自定义的切换效果:

@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.enter_anim, R.anim.exitAnim);
}

Fragment也可以添加切换效果,可以通过FragmentTransaction中的setCustomAnimations()方法来添加切换动画,这个切换动画需要时View动画。还有其他的方法为Activity和Fragment添加切换动画,但是需要考略兼容为题。

  • 属性动画

属性动画是API11新加入的特性,和View动画不同,它对作用的对象进行扩展,属性动画可以对任何对象做动画,甚至还可以没有对象。属性动画可以对任意对象的属性进行动画而不仅仅是View。属性动画中有ValueAnimator, ObjectAnimator和AnimatorSet等概念。为兼容之前的版本,采用nineoldandroids源动画库。

ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合。属性动画需要定义在res/animator/目录下。

  1. 改变一个对象myObject的translationY属性,让其沿着Y轴向上移动一段距离,在默认的时间完成。
    1. ObjectAnimator.ofFloat(tv, "translationY", -myObject.getHeight())
      .start();
  2. 改变一个对象背景色属性,典型的改变View背景,下面的动画可以让背景色在3s内实现从0xFFFF8080到0xFF8080FF的渐变,动画会无限循环而且会有反转的效果。
    1. ValueAnimator colorAnim = ObjectAnimator.ofInt(myObject, "backgroundColor", 0xFFFF8080, 0xFF8080FF);
      colorAnim.setDuration(3000);
      colorAnim.setEvaluator(new ArgbEvaluator());
      colorAnim.setRepeatCount(ValueAnimator.INFINITE);
      colorAnim.setRepeatMode(ValueAnimator.REVERSE);
      colorAnim.start();
  3. 动画集合,5秒内对View的旋转,平移,缩放和透明度都进行改变。
    1. AnimatorSet set = new AnimatorSet();
      set.playTogether(
      ObjectAnimator.ofFloat(view, "rotationX", 0, 360),
      ObjectAnimator.ofFloat(view, "rotationY", 0, 180),
      ObjectAnimator.ofFloat(view, "rotation", 0, -90),
      ObjectAnimator.ofFloat(view, "translationX", 0, 100),
      ObjectAnimator.ofFloat(view, "translationY", 0, 100),
      ObjectAnimator.ofFloat(view, "scaleX", 1, 1.5f),
      ObjectAnimator.ofFloat(view, "scaleY", 1, 0.5f),
      ObjectAnimator.ofFloat(view, "alpha", 1, 0.5f, 1)
      );
      set.setDuration(5 * 1000).start();

属性动画除了代码实现之外,还可以通过XML来定义。

eg.

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator android:propertyName="x"
android:duration="300"
android:valueTo="300"
android:valueType="intType" /> <objectAnimator android:propertyName="y"
android:duration="300"
android:valueTo="300"
android:valueType="intType"/>
</set>

加载上面的属性动画:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.property_animator);
set.setTarget(view);
set.start();

建议:属性动画采用代码来实现,这是因为代码的方式会比较简单,更重要的是,在一个属性起始值无法确定的情况下,将无法将属性定义在xml文件中。

  • 插值器和估值器

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

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

线性差值器,属于匀速动画,其源码为:

/**
* An interpolator where the rate of change is constant
*/
@HasNativeInterpolator
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();
}
}

那到底输入数值是什么?这需要估值算法来确定,如整型估值器的源码为:

public class IntEvaluator implements TypeEvaluator<Integer> {

    /**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>int</code> or
* <code>Integer</code>
* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}

evaluate方法的三个参数分别是估值小数,起始值,结束值。例子:匀速动画,对象从0位置到100,当估值小数位0.5的时候,根据算法result = 0 + 0.5(100 - 0) = 50。

属性动画要求对象有该属性的set方法和get方法(可选)。除了系统预置的插值器和估值算法外,我们还可以自定义。自定义差值算法需要实现Interpolator或TimeInterpolator;自定义估值算法需要实现TypeEvaluator。

属性动画的监听:

有两个常用的属性动画监听:AnimatorListener, AnimatorUpdateListener

ObjectAnimator animator = new ObjectAnimator();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {} @Override
public void onAnimationEnd(Animator animation) {} @Override
public void onAnimationCancel(Animator animation) {} @Override
public void onAnimationRepeat(Animator animation) {}
});

从AnimatorListener的定义来看,它可以监听动画的开始,结束,取消和重复动画。

public static interface AnimatorUpdateListener {
/**
* <p>Notifies the occurrence of another frame of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationUpdate(ValueAnimator animation); }

AnimatorUpdateListener比较特殊,它会监听整个动画过程,动画是由许多帧组成的,每播放一帧onAnimationUpdate()就会被调用一次,利用这个特征,我们可以做一些特殊事情。

  • 对任意属性做动画

例如TextView的宽度动画变为800的动画,当layout_width的属性为wrap_content时,是有动画的,但是如果设置为固定值之后,下面的属性动画就没有效果了。

//高度从原来的默认值变为800
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(textView, "width", 800);
objectAnimator.setDuration(500);
objectAnimator.start();

这是因为:

public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS; requestLayout();
invalidate();
}
 public final int getWidth() {
return mRight - mLeft;
}

setWidth不是所谓意义上的宽度设置,所以就不会产生动画。对任意属性做动画,一般需要有set和get方法,设置流程如下:

如果没有属性设置获取权限,可以定义一个包裹类进行封装,实现set和get方法;如:

public static class ViewWrapper{

        private View mTarget;

        public ViewWrapper(View target){
mTarget = target;
} public void setWidth(int width){
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
} public int getWidth(){
return mTarget.getLayoutParams().width;
}
}
ViewWrapper viewWrapper = new ViewWrapper(view);
ObjectAnimator widthAnimator = ObjectAnimator.ofInt(viewWrapper, "width", btn.getWidth(), 800)
.setDuration(500);
widthAnimator.start();

这样,上面的TextView就可以实现宽度的动画效果了。

自定义View设置相关属性,进行动画案例:

/**
* 自定义圆环
* 对属性progress设置set和get方法
* 就可以对CircleProgress的progress进行属性动画
*/
public class CircleProgress extends View{ private Paint mPaint;
private RectF rectF;
private int progress = 270;
int width, height; public CircleProgress(Context context) {
super(context);
init();
} public CircleProgress(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
} public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} private void init(){
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(20);
mPaint.setColor(Color.RED);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY){
width = widthSize;
}else{
width = 100; //设置为默认值
} if (heightMode == MeasureSpec.EXACTLY){
height = heightSize;
}else{
height = 100; //设置为默认值
}
rectF = new RectF(20, 20, width-20, height-20);
setMeasuredDimension(Math.min(width, height), Math.min(width, height)); //矩形
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(rectF, -90, progress, false, mPaint);
invalidate();
} public void setProgress(int progress){
this.progress = progress;
} public int getProgress(){
return progress;
}
}

Android之动画的更多相关文章

  1. Android属性动画

    这几天看郭神的博客 Android属性动画完全解析(上),初识属性动画的基本用法之后,我自己突然想实现一种动画功能,就是我们在携程网.阿里旅行等等手机APP端买火车票的时候,看到有选择城市,那么就有出 ...

  2. android 自定义动画

    android自定义动画注意是继承Animation,重写里面的initialize和applyTransformation,在initialize方法做一些初始化的工作,在applyTransfor ...

  3. 【转】android 属性动画之 ObjectAnimator

    原文网址:http://blog.csdn.net/feiduclear_up/article/details/39255083 前面一篇博客讲解了 android 简单动画之 animtion,这里 ...

  4. Android属性动画之ValueAnimation

    ValueAnimation是ObjectAnimation类的父类,经过前几天的介绍,相信大家对ObjectAnimation有了 一定的认识,今天就为大家最后介绍一下ValueAnimation, ...

  5. Android属性动画之ObjectAnimator

    相信对于Android初学者,对于Android中的动画效果一定很感兴趣,今天为大家总结一下刚刚学到的属性动画案例. 首先和一般的Android应用一样,我们先建一个工程,为了方便,我们的布局文件中就 ...

  6. 79.Android之动画基础

    转载:http://a.codekk.com/detail/Android/lightSky/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8 ...

  7. Android属性动画完全解析(下)

    转载:http://blog.csdn.net/guolin_blog/article/details/44171115 大家好,欢迎继续回到Android属性动画完全解析.在上一篇文章当中我们学习了 ...

  8. Android属性动画完全解析(上),初识属性动画的基本用法

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/43536355 在手机上去实现一些动画效果算是件比较炫酷的事情,因此Android系 ...

  9. Android属性动画完全解析(中)

    转载:http://blog.csdn.net/guolin_blog/article/details/43536355 大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是 ...

  10. Android属性动画完全解析(上)

    Android属性动画完全解析(上) 转载:http://blog.csdn.net/guolin_blog/article/details/43536355 在手机上去实现一些动画效果算是件比较炫酷 ...

随机推荐

  1. 听说你又一次放弃了Dagger2,来听老夫说道说道

    关于Dagger2是啥,用法网上已经有很多了.不过听说你已经很多次Dagger2从入门到放弃了,来来,让我这个老中医给你治一治.老夫手法纯熟,不会让你有任何痛苦的,放心读下去吧. 本文同步自 博主的私 ...

  2. 【unix网络编程第三版】ubuntu端口占用问题

    <unix网络编程>一书中的代码并不是能直接运行,有时候需要结合各方面的知识来解决,大家在这本书的时候,一定要把代码都跑通,不难你会错过很多学习的机会! 1.问题描述 本人在阅读<U ...

  3. “XcodeGhost”病毒之后,苹果更应注…

    虽然大家都在期待中秋假期的到来,不过让开发者挺闹心的一件事就是这几天网上.朋友圈以及各种群中炒得沸沸扬扬的"XcodeGhost"病毒事件,就连央视也惊动了!! 事件起源 事件起源 ...

  4. PageContext ServletContext ServletConfig辨析

    上面三个东西都是什么关系呀? 先看图 注意几点 1 GenericServlet有两个init方法# 2 GenericServlet既实现了ServletConfig方法,它自己由依赖一个Servl ...

  5. 【leetcode73】经典算法-Guess Number Higher or Lower

    题目描述: 从1-n中,随便的拿出一个数字,你来猜测. 提示 提供一个guess(int num)的api,针对猜测的数字,返回三个数值.0,-1,1 0;猜中返回num -1:比猜测的数值小 1:比 ...

  6. Zookeeper实现数据的发布和订阅

    使用场景        当一个对象的改变,需要通知其他对象而且不知道要通知多少个对象,可以使用发布订阅模式 .在分布式中的应用有配置管理(Configuration Management) .集群管理 ...

  7. JSP之Cookie的实现

    在我们浏览网页的时候,经常会看到自己曾经浏览过的网页的具体的一些信息,那这些究竟是通过什么来实现的呢?难道是有人在监视我们的电脑吗?其实不是的,实现这一功能就是利用了我们接下来看到的cookie技术. ...

  8. 跨平台移动APP开发进阶(一)mui开发注意事项

    mui开发注意事项 Mui HTML5开发框架 mui是一个高性能的HTML5开发框架,从UI到效率,都在极力追求原生体验:这个框架自身有一些规则,刚接触的同学不很熟悉,特总结本文:想了解mui更详细 ...

  9. myeclipse 彻底让烦人的各种验证消失 让你的开发速度飞快

    大家都知道,myeclipse的验证很吭爹,不但保存的时候要难,BUILD的时候也要验.常常为了等它而浪费了大浪的时间!!真不知道设计人员当初是怎么加进这种功能的.真心不需要. 以前都是到window ...

  10. Java进阶(五十一)Could not create the view: An unexpected exception was thrown

    Java进阶(五十一)Could not create the view: An unexpected exception was thrown 今天打开Myeclipse10的时候,发现server ...