Android动画分析(对任意属性做动画效果)

上篇文章中,我们曾经提到过,ObjectAnimator实现动画效果是,可以这样写:

  ObjectAnimator
                .ofFloat(myView, "rotationX", 0.0F, 360.0F)
                .setDuration(500)//
                .start();

上篇文章说过,第二个参数是指属性的名字,例如rotationX,但是如果我们随便写个值的话,轻者则无动画效果,重者可能会崩溃,为什么呢?今天就来说说原因


举个例子,给Button加一个动画,让这个Button的宽度从当前宽度增加到500,也许你可能会这样想:

  ObjectAnimator
                .ofFloat(mButton, "width", 500)
                .setDuration(500)//
                .start();

可是,当运行时,你会发现没有任何效果,为什么呢?其实,我们如果知道属性动画的原理,就很容易理解了


属性动画原理

属性动画要求做动画效果的属性需要提供get和set的方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递set方法的值都不一样,随着时间的推移,越来越接近最终值,所以,总的来说,需要动画生效,需满足两个条件:

(1)对象的属性必须提供set和get方法
(2)set方法可以带来效果,比如我setWidth后,宽度会带来UI的变化,否则你也看不出变化是吧

在回到我们的例子上,有人可能会说,Button提供了setWidth方法啊,为什么还是不行呢?别着急,我们来看看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();
    }

Button是继承TextView,setWidth是TextView和子类专属的方法,我们从源码中不难看出,它的作用不是设置宽度,而是设置最大宽度和最小宽度,也就是说跟我们想要设置Button宽度是两个东西,具体来说,TextView的宽度对应的是xml中的android:layout_width,而setWidth对应的是xml中的android:width,所以没有动画效果也是正常的,那如果我的需求就是类似这样的情况,我们怎么办呢?别着急,谷歌早就帮我们想好了

类似这样的情况,官方文档告诉我们有3种解决方法:
(1)给属性加上get和set的方法
(2)用一个类来封装原始对象,并提供get和set方法
(3)采用ValueAnimator,监听动画过程,自己实现属性的变化

首先我们来分别看看这几个方法:

方法1:

我们首先就可以排除了,我们不可能去修改Button的源码,给它加上get和set方法

方法2:

是一个很有用的解决方法,也是最普遍的,很方便也很好理解,我们看看例子

public class MyButton {
    private View mView;

    public MyButton(View view) {
        this.mView = view;
    }

    public int getWidth() {
        return mView.getWidth();
    }

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

我们先定义一个类来封装我们的Button,然后提供get和set方法,再来我们就可以调用了:

 MyButton button =new MyButton(new Button(this));
 ObjectAnimator.ofInt(button,"width",500).setDuration(500).start();

没错,就是如此简单

方法3

如果大家对ValueAnimator熟悉的话就知道,它不作用于任何对象,也就是说直接使用它是没有动画效果的,我们需要它对一个值做动画,然后监听变化过程,在变化过程中修改我们对象的属性值,就相当于我们对象做动画了

ValueAnimator animator = ValueAnimator.ofInt(1, 100);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //获取当前动画进度值,1-100之间
                int  value = (int) animation.getAnimatedValue();
                //获取当前进度占整个动画过程比例,0-1之间
                float fraction = animation.getAnimatedFraction();
                //直接调用估值器,通过比例计算宽度,然后再赋值给Button
                IntEvaluator evaluator =new IntEvaluator();
                mButton.getLayoutParams().width=evaluator.evaluate(fraction,mButton.getWidth(),500);
                mButton.requestLayout();
            }
        });
animator.setDuration(500).start();

它在500ms内将一个数从1到100,然后动画的每一帧都会回调onAnimationUpdate方法,因此,我们可以获取当前的值和当前值所占的比例,在计算出Button当前的宽度应该是多少,比如时间过了一半,当前值是50,比例是0.5,假设Button的起始宽度是100,最终宽度是500,那么Button增加的宽度也应该占总增加宽度的一半,总增加的宽度是500-100=400,所以这时候Button应该增加的宽度是400*0.5=200,那么当前Button的宽度应该为初始宽度+增加宽度(100+200=300),这个计算IntEvaluator 估值器已经帮我们实现了,因此我们直接用就可以了


注意事项

动画可以为我们带来很多炫酷的效果,但同时也有很多需要注意的地方,主要分为这几类:

OOM

这个问题主要出现在帧动画中,因为帧动画是用多张图片轮播实现动画效果,那么如果图片过大就很容易出现OOM

内存泄漏

在属性动画中,有一类无限循环的动画,这类动画需要在Activity退出的时候及时停止,否则会导致无法释放从而导致内存泄漏,View动画没存在这问题

兼容性

动画是3.0以下的系统兼容性会存在问题,因此需要做好兼容工作

View动画的问题

View动画是对view的影像做动画,并不是真正的去改变view的状态,因此有时候会出现动画完成后view无法隐藏的现象,即setVisibility(GONE)失效,这时候只要调用view.clearAnimation()清除view动画即可

不要使用px

在进行动画过程中,单位尽量用dp,使用px可能导致在不同设备上效果不一样

动画过后交互

view动画还是属性动画效果完成后,view的位置是不会发生改变的,也就是说view的位置还是在原来的位置,尽管view的视觉已经改变,位置是不发生变化,仅仅只是视觉的变化


好了,这篇文章到这就结束了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容