【转】Android Android属性动画深入分析

转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17841165

开篇

像设计模式一样,我们也提出一个问题来引出我们的内容。

问题:

给Button加一个动画,让这个Button的宽度从当前宽度增加到500px。

也许你会说,这很简单,用渐变动画就可以搞定,我们可以来试试,你能写出来吗?很快你就会恍然大悟,原来渐变动画根本不支持对宽度进行动画啊,没 错,渐变动画只支持四种类型:平移(Translate)、旋转(Rotate)、缩放(Scale)、不透明度(Alpha)。当然你用x方向缩放 (scaleX)可以让Button在x方向放大,看起来好像是宽度增加了,实际上不是,只是Button被放大了而已,而且由于只在x方向被放大,这个 时候Button的背景以及上面的文本都被拉伸了,甚至有可能Button会超出屏幕。下面是效果图

上述效果显然是很差的,而且也不是真正地对宽度做动画,不过,所幸我们还有属性动画,我们用属性动画试试

看demo

 1     private void performAnimate() {
 2         ObjectAnimator.ofInt(mButton, "width", 500).setDuration(5000).start();
 3     }
 4
 5     @Override
 6     public void onClick(View v) {
 7         if (v == mButton) {
 8             performAnimate();
 9         }
10     }

上述代码运行一下发现没效果,其实没效果是对的,如果你随便传递一个属性过去,轻则没动画效果,重则程序直接Crash。

***就上面的例子来说,如果你在XML文件中对Button的android:width和android:height的设置为wrap_content是有效果的,但是你如果对android:width和android:height设置为具体的值(100dp等)就会没有效果的

下面分析下属性动画的原理:

属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据你传递的该熟悉的初始值和最终值,以动画的效果多次去调用set方法,每 次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。总结一下,你对object的属性xxx做动画,如果想让动画 生效,要同时满足两个条件:

1. object必须要提供setXxx方法,如果动画的时候没有传递初始值,那么还要提供getXxx方法,因为系统要去拿xxx属性的初始值(如果这条不满足,程序直接Crash)

2. object的setXxx对属性xxx所做的改变必须能够通过某种方法反映出来,比如会带来ui的改变啥的(如果这条不满足,动画无效果但不会Crash)

以上条件缺一不可

那么为什么我们对Button的width属性做动画没有效果?这是因为Button内部虽然提供了getWidth和setWidth方法,但是 这个setWidth方法并不是改变视图的大小,它是TextView新添加的方法,View是没有这个setWidth方法的,由于Button继承了 TextView,所以Button也就有了setWidth方法。下面看一下这个getWidth和setWidth方法的源码:

 1     /**
 2      * Makes the TextView exactly this many pixels wide.
 3      * You could do the same thing by specifying this number in the
 4      * LayoutParams.
 5      *
 6      * @see #setMaxWidth(int)
 7      * @see #setMinWidth(int)
 8      * @see #getMinWidth()
 9      * @see #getMaxWidth()
10      *
11      * @attr ref android.R.styleable#TextView_width
12      */
13     @android.view.RemotableViewMethod
14     public void setWidth(int pixels) {
15         mMaxWidth = mMinWidth = pixels;
16         mMaxWidthMode = mMinWidthMode = PIXELS;
17
18         requestLayout();
19         invalidate();
20     }
21
22     /**
23      * Return the width of the your view.
24      *
25      * @return The width of your view, in pixels.
26      */
27     @ViewDebug.ExportedProperty(category = "layout")
28     public final int getWidth() {
29         return mRight - mLeft;
30     }

从源码可以看出,getWidth的确是获取View的宽度的,而setWidth是TextView和其子类的专属方法,它的作用不是设置 View的宽度,而是设置TextView的最大宽度和最小宽度的,这个和TextView的宽度不是一个东西,具体来说,TextView的宽度对应 Xml中的android:layout_width属性,而TextView还有一个属性android:width,这个android:width 属性就对应了TextView的setWidth方法。好吧,我承认我的这段描述有点混乱,但事情的确是这个样子的,而且我目前还没发现这个 android:width属性有啥重要的用途,感觉好像没用似的,这里就不深究了,不然就偏离主题了。总之,TextView和Button的 setWidth和getWidth干的不是同一件事情,通过setWidth无法改变控件的宽度,所以对width做属性动画没有效果,对应于属性动画 的两个条件来说,本例中动画不生效的原因是只满足了条件1未满足条件2。

针对上述问题,Google告诉我们有3中解决方法:

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

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

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

看起来有点抽象,不过不用担心,下面我会一一介绍。

对任何属性做动画

针对上面提出的三种解决方法,这里会给出具体的介绍:

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

这个的意思很好理解,如果你有权限的话,加上get和set就搞定了,但是很多时候我们没权限去这么做,比如本文开头所提到的问题,你无法给 Button加上一个合乎要求的setWidth方法,因为这是Android SDK内部实现的。这个方法最简单,但是往往是不可行的,这里就不对其进行更多分析了。

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

这是一个很有用的解决方法,是我最喜欢用的,因为用起来很方便,也很好理解,下面将通过一个具体的例子来介绍它

 1     private void performAnimate() {
 2         ViewWrapper wrapper = new ViewWrapper(mButton);
 3         ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
 4     }
 5
 6     @Override
 7     public void onClick(View v) {
 8         if (v == mButton) {
 9             performAnimate();
10         }
11     }
12
13     private static class ViewWrapper {
14         private View mTarget;
15
16         public ViewWrapper(View target) {
17             mTarget = target;
18         }
19
20         public int getWidth() {
21             return mTarget.getLayoutParams().width;
22         }
23
24         public void setWidth(int width) {
25             mTarget.getLayoutParams().width = width;
26             mTarget.requestLayout();
27         }
28     }

上述代码5s内让Button的宽度增加到500px,为了达到这个效果,我们提供了ViewWrapper类专门用于包装View,具体到本例是 包装Button,然后我们对ViewWrapper的width熟悉做动画,并且在setWidth方法中修改其内部的target的宽度,而 target实际上就是我们包装的Button,这样一个间接属性动画就搞定了。上述代码同样适用于一个对象的其他属性。下面看效果

***上述的代码可能执行的效果是Button先闪一下变成500px长度,然后再慢慢变化,这个原因是没有给ObjectAnimator设置Button变化的起始值。将ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();

改成ObjectAnimator.ofInt(wrapper, "width",btn.getWidth(),500).setDuration(5000).start();就好了

ok,效果达到了,真正实现了对宽度做动画。

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

首先说说啥是ValueAnimator,ValueAnimator本身不作用于任何对象,也就是说直接使用它没有任何动画效果。它可以对一个值
做动画,然后我们可以监听其动画过程,在动画过程中修改我们的对象的属性值,这样也就相当于我们的对象做了动画。还是不太明白?没关系,下面用例子说明

 1     private void performAnimate(final View target, final int start, final int end) {
 2         ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
 3
 4         valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
 5
 6             //持有一个IntEvaluator对象,方便下面估值的时候使用
 7             private IntEvaluator mEvaluator = new IntEvaluator();
 8
 9             @Override
10             public void onAnimationUpdate(ValueAnimator animator) {
11                 //获得当前动画的进度值,整型,1-100之间
12                 int currentValue = (Integer)animator.getAnimatedValue();
13                 Log.d(TAG, "current value: " + currentValue);
14
15                 //计算当前进度占整个动画过程的比例,浮点型,0-1之间
16                 float fraction = currentValue / 100f;
17
18                 //这里我偷懒了,不过有现成的干吗不用呢
19                 //直接调用整型估值器通过比例计算出宽度,然后再设给Button
20                 target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
21                 target.requestLayout();
22             }
23         });
24
25         valueAnimator.setDuration(5000).start();
26     }
27
28     @Override
29     public void onClick(View v) {
30         if (v == mButton) {
31             performAnimate(mButton, mButton.getWidth(), 500);
32         }
33     }

上述代码的动画效果图和采用ViewWrapper是一样的,请参看上图。关于这个ValueAnimator我要再说一下,拿上例来说,它会在 5000ms内将一个数从1变到100,然后动画的每一帧会回调onAnimationUpdate方法,在这个方法里,我们可以获取当前的值 (1-100),根据当前值所占的比例(当前值/100),我们可以计算出Button现在的宽度应该是多少,比如时间过了一半,当前值是50,比例为 0.5,假设Button的起始宽度是100px,最终宽度是500px,那么Button增加的宽度也应该占总增加宽度的一半,总增加宽度是 500-100=400,所以这个时候Button应该增加宽度400*0.5=200,那么当前Button的宽度应该为初始宽度+ 增加宽度(100+200=300)。上述计算过程很简单,其实它就是整型估值器IntEvaluator的内部实现,所有我们不用自己写了,直接用吧。

写在后面的话

到此为止,本文的分析基本完成,有几点是我想再说一下的。

1.View动画(渐变动画)的功能是有限的,大家可以尝试使用属性动画

2.为了在各种安卓版本上使用属性动画,你需要采用nineoldandroids,它是GitHub开源项目,jar包和源码都可以在网上下到,如果下不到jar包,我可以发给大家

3.再复杂的动画都是简单动画的合理组合,再加上本文介绍的方法,可以对任何属性作用动画效果,也就是说你几乎可以做出任何动画

4.属性动画中的插值器(Interpolator)和估值器(TypeEvaluator)很重要,它是实现非匀速动画的重要手段,你应该试着搞懂它,最好你还能够自定义它们

时间: 2024-10-03 23:27:07

【转】Android Android属性动画深入分析的相关文章

android学习(属性动画扇形菜单栏)

1 public class TestActivity extends Activity implements OnClickListener{ 2 private int[] res={R.id.imageView1,R.id.imageView2,R.id.imageView3,R.id.imageView4,R.id.imageView5,R.id.imageView6}; 3 private List<ImageView> list=new ArrayList<ImageView

android的属性动画

原文作者:Google 原文地址:https://developer.android.com/guide/topics/graphics/prop-animation.html 原文版权:Creative Commons 2.5 Attribution License 译文作者:Jianan - [email protected] 版本信息:本文基于2016-06-17版本翻译 译文版权:CC BY-NC-ND 4.0,允许复制转载,但必须保留译文作者署名及译文链接,不得演绎和用于商业用途 前言

Android用属性动画拖动view到任意位置

直接上图: 代码: package me.waye; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.ImageVie

Android之属性动画(一)

一.概述 Android平台中常用的动画主要有两类,一类是View动画,一类是3.0后新增的属性动画.属性动画与View动画相比功能更加强大,主要体现在以下两个方面: 1.  属性动画不仅仅能应用到View上,还可以应用到Object对象上. 2.  属性动画将会真正改变Object对象属性的值. 如此强大的动画,我们没有理由不去学习使用,下面我们就根据API文档的介绍开始学习如何使用属性动画. 二.属性动画的学习与使用 首先我们要先了解属性动画的一些关键属性,文档中给出了这么几个: Durat

Android之属性动画、值动画

Android属性动画 什么是Android属性动画 同类技术对比 补间动画Tween Animation 帧动画Frame Animation 属性动画Property Animation 属性动画组成部分相关类介绍 ObjectAnimator对象动画执行类 介绍 示例 ValueAnimator 值动画执行类 介绍 示例 PropertyValuesHolder 属性存储器 介绍 示例 Keyframe 关键帧 介绍 示例 AnimatorSet 执行集合类 介绍 示例 AnimatorU

android之属性动画和布局动画详解

1.属性动画是什么不废话,不懂的可以百度一下参考郭霖大神的动画详解篇:这里仅仅提供一个demo说说用法,抛砖引玉,代码的注释写的已经很详细,不再多说废话,一下提供的是一个基础的demo,讲解的是objectAnimator的基础用法,如平移.旋转.缩放.渐变以及动画的集合:至于objectAnimator(必须的有set get方法)和valueAnimator的详细区别也可参考郭霖大神的动画详解篇 2.除此基本用法,还有估值器和插值器 (1)插值器:动画速率的变换,有点类似物理的加速度,就是该

Android进阶——属性动画Property Animation详解(一)

引言 前一篇文章Android入门--补间动画和帧动画应用小结总结了补间动画和帧动画及一些相关类的应用,基本可以掌握简单的缩放.旋转.透明度变化.平移的动画效果,但是需要实现更复杂的动画效果时,比如说希望View的切换动画.Layout的切换动画.3D旋转动画等等,这些View Animation都无法做到.此时Property Animation应运而生,这篇主要总结下属性动画的相关知识点. 一.Property Animation属性动画概述 属性动画,在我最先接触到Android 1.5时

Android:属性动画详解

(一)简介 属性动画是Android 3.0中提供了新功能,便于对任意元素进行"平滑的过渡".众所周知,Android 之前提供的补间动画如AlphaAnimation.TranslateAnimation等都只能应用在View上,并且几乎没有任何可扩展的地方.并且,使用原来的动画无法实现颜色渐变效果,算是一个遗憾. 属性动画彻底打破了这种设计方式,不再将这些动画强制的放在View上,而是仅仅产生一系列的值,由开发人员自行使用这些值.例如,属性动画可产生1-0平滑过渡的值,把这些值应用

Android 用属性动画自定义view的渐变背景

自定义view渐变背景,同时监听手势自动生成小圆球. 宿主Activity如下: package com.edaixi.tempbak; import java.util.ArrayList; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ArgbE