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

开篇

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

问题:

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

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

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

看demo

     private void performAnimate() {
ObjectAnimator.ofInt(mButton, "width", 500).setDuration(5000).start();
} @Override
public void onClick(View v) {
if (v == mButton) {
performAnimate();
}
}

上述代码运行一下发现没效果,其实没效果是对的,如果你随便传递一个属性过去,轻则没动画效果,重则程序直接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方法的源码:

     /**
* Makes the TextView exactly this many pixels wide.
* You could do the same thing by specifying this number in the
* LayoutParams.
*
* @see #setMaxWidth(int)
* @see #setMinWidth(int)
* @see #getMinWidth()
* @see #getMaxWidth()
*
* @attr ref android.R.styleable#TextView_width
*/
@android.view.RemotableViewMethod
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS; requestLayout();
invalidate();
} /**
* Return the width of the your view.
*
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}

从源码可以看出,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方法

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

     private void performAnimate() {
ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
} @Override
public void onClick(View v) {
if (v == mButton) {
performAnimate();
}
} private static class ViewWrapper {
private View mTarget; public ViewWrapper(View target) {
mTarget = target;
} public int getWidth() {
return mTarget.getLayoutParams().width;
} public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}

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

     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 = currentValue / 100f; //这里我偷懒了,不过有现成的干吗不用呢
//直接调用整型估值器通过比例计算出宽度,然后再设给Button
target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
target.requestLayout();
}
}); valueAnimator.setDuration(5000).start();
} @Override
public void onClick(View v) {
if (v == mButton) {
performAnimate(mButton, mButton.getWidth(), 500);
}
}

上述代码的动画效果图和采用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)很重要,它是实现非匀速动画的重要手段,你应该试着搞懂它,最好你还能够自定义它们

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

  1. android使用属性动画代替补间动画

    本文参考Android属性动画完全解析(上),初识属性动画的基本用法 android3.0之前一共有两种动画,分别是frame动画和tween动画,关于这两种动画如果不了解可以查看我之前的文章andr ...

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

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

  3. 【Android】属性动画

    转载请注明出处:http://blog.csdn.net/h28496/44338669 属性动画的原理 通过不断的设置一个View的属性让其出现动画效果.比如,不断地设置一个Button的x值.这个 ...

  4. Android之属性动画(二)

    上一篇文章(链接:http://www.cnblogs.com/jerehedu/p/4458928.html  ),我们对属性动画有了简单的认识,并实际动手使用ObjectAnimator.Anim ...

  5. Android之属性动画(一)

    一.概述 Android平台中常用的动画主要有两类,一类是View动画,一类是3.0后新增的属性动画.属性动画与View动画相比功能更加强大,主要体现在以下两个方面: 1.  属性动画不仅仅能应用到V ...

  6. Android使用属性动画ValueAnimator动态改变SurfaceView的背景颜色

    以下是主要代码,难点和疑问点都写在注释中: /** * 开始背景动画(此处为属性动画) */ private void startBackgroundAnimator(){ /* *参数解释: *ta ...

  7. Android开发属性动画

    普通动画效果和属性动画效果区别: 普通动画效果的动画播放后只是产生了视觉欺骗,并没有移动真实的控件. 属性动画直接真实的移动控件 AnimationSet动画: TextView t1 = (Text ...

  8. Android ViewPager+属性动画 实现炫酷视差动画效果

    ViewPager有一个setPageTransform()方法可以实现很多酷炫的动画效果 先来个仿QQ的侧滑面板效果 vp.setPageTransformer(true, new PageTran ...

  9. Android属性动画源代码解析(超详细)

    本文假定你已经对属性动画有了一定的了解,至少使用过属性动画.下面我们就从属性动画最简单的使用开始. ObjectAnimator .ofInt(target,propName,values[]) .s ...

随机推荐

  1. Oracle 12.1.0.2 对JSON的支持

    Oracle 12.1.0.2版本有一个新功能就是可以存储.查询.索引JSON数据格式,而且也实现了使用SQL语句来解析JSON,非常方便.JSON数据在数据库中以VARCHAR2, CLOB或者BL ...

  2. (转)如何修正DIV float之后导致的外部容器不能撑开的问题

    本文转载自:http://blog.csdn.net/qsdnet/article/details/1534005 在写HTML代码的时候,发现在Firefox等符合W3C标准的浏览器中,如果有一个D ...

  3. AngularJS:表格

    ylbtech-AngularJS:表格 1.返回顶部 1. AngularJS 表格 ng-repeat 指令可以完美的显示表格. 在表格中显示数据 使用 angular 显示表格是非常简单的: A ...

  4. Cassandra 学习七 cassandra研究

    https://www.cnblogs.com/bonelee/p/6306079.html Allow filtering: 如果你的查询条件里,有一个是根据索引查询,那其它非索引非主键字段,可以通 ...

  5. ZedGraph 总论

       ZedGraph 总论            ZedGraph 是一个开源的.NET图表类库, 并且全部代码都是用C#开发的.它可以利用任意的数据集合创建2D的线性和柱形图表.          ...

  6. springboot成神之——swagger文档自动生成工具

    本文讲解如何在spring-boot中使用swagger文档自动生成工具 目录结构 说明 依赖 SwaggerConfig 开启api界面 JSR 303注释信息 Swagger核心注释 User T ...

  7. Windows修改MySQL用户root密码

    MySQL是一个关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软 ...

  8. HDFS的介绍

    设计思想 分而治之:将大文件.大批量文件,分布式存放在大量服务器上,以便于采取分而治之的方式对海量数据进行运算分析: 在大数据系统中作用:为各类分布式运算框架(如:mapreduce,spark,te ...

  9. 魔戒(思维+bfs)

    Description 蓝色空间号和万有引力号进入了四维水洼,发现了四维物体--魔戒. 这里我们把飞船和魔戒都抽象为四维空间中的一个点,分别标为 "S" 和 "E&quo ...

  10. codeforces:Roads in the Kingdom分析和实现

    题目大意:国家有n个城市,还有n条道路,每条道路连通两个不同的城市,n条道路使得所有n个城市相互连通.现在国家经费不足,要关闭一条道路.国家的不便度定义为国家中任意两个不同的城市之间的距离的最大值,那 ...