属性动画的基本使用

  • 属性动画简介

Android开发过程中,适当的使用一些动画可以让自己的应用看起来更棒更炫。最初的时候Google为了实现动画,主要提供了两种基本动画:
帧动画:将一个完整的动画拆分成一张张单独的图片,然后再将它们连贯起来进行播放
补间动画:无需逐一定义每一帧,只要定义开始、结束的帧,和指定动画持续时间。
到了Android3.0,Google推出了一种新的动画模式,属性动画。属性动画是对补间动画的进一步完善和强化。

  • 为什么引入属性动画:

关于这个问题,可以借鉴一下郭霖的一段话:
补间动画机制其实还算是比较健全的,可以让我们实现View的移动、缩放、旋转和淡入淡出效果,并且我们还可以借助AnimationSet来将这些动画效果组合起来使用,除此之外还可以通过配置Interpolator来控制动画的播放速度等等等等。那么这里大家可能要产生疑问了,既然之前的动画机制已经这么健全了,为什么还要引入属性动画呢?
其实上面所谓的健全都是相对的,如果你的需求中只需要对View进行移动、缩放、旋转和淡入淡出操作,那么补间动画确实已经足够健全了。但是很显然,这些功能是不足以覆盖所有的场景的,一旦我们的需求超出了移动、缩放、旋转和淡入淡出这四种对View的操作,那么补间动画就不能再帮我们忙了,也就是说它在功能和可扩展方面都有相当大的局限性,那么下面我们就来看看补间动画所不能胜任的场景。
注意上面我在介绍补间动画的时候都有使用“对View进行操作”这样的描述,没错,补间动画是只能够作用在View上的。也就是说,我们可以对一个Button、TextView、甚至是LinearLayout、或者其它任何继承自View的组件进行动画操作,但是如果我们想要对一个非View的对象进行动画操作,抱歉,补间动画就帮不上忙了。可能有的朋友会感到不能理解,我怎么会需要对一个非View的对象进行动画操作呢?这里我举一个简单的例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的第一个缺陷。
然后补间动画还有一个缺陷,就是它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?很遗憾,我们只能靠自己去实现了。说白了,之前的补间动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。
最后,补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。正是因为这些原因,功能更为完善和强大的属性动画就应运而生了。

  • 继承关系
继承关系

从图中可以看出,补间动画主要是使用android.view.animation.Animation包下面的五个类来实现不同的动画效果;而属性动画主要是使用android.animation.Animator的ValueAnimator以及其子类ObjectAnimator来实现不同的动画效果。

  • ValueAnimator

从属性动画相关对象的继承关系可以看出,ValueAnimator是比较核心的一个类。官方文档的介绍是:this class provides a simple timing engine for running animations which calculate animated values and set them on target objects...也就是说ValueAnimator对象内部维持了一个简单的时间引擎,计算运行动画的具体值同时把它们设置给目标对象。通俗的讲就是,ValueAnimator的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值或者中间值提供给ValueAnimator,并且设置动画运行时长,那么ValueAnimator就会自动完成从初始值平滑过渡到结束值这样的效果。而且,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。说的废话比较多,先举个栗子,切身体会一下!

1.效果图

ValueAnimator

2.具体代码

ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
valueAnimator.setDuration(3000);
//设置监听,把当前AnimatedValue的值时时显示到EditText上面
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float currentValue = (float) animation.getAnimatedValue();
        StringBuilder sb = new StringBuilder();
        et_Animator.setText(sb.append(currentValue).toString());
    }
});
valueAnimator.start();

ValueAnimator比较常用的方法有:设置AnimatorValue区间范围的三个主要方法
ofFloat(),ofInt(),ofObject()
还有设置延时播放时间的setStartDelay()方法,设置重复次数的setRepeatCount()方法,设置重复模式的setRepeatMode()
暂停动画的pause()方法,恢复动画的resume()等等,还有很多方法,具体大家可以查阅文档,把这个核心类的方法掌握了,使用ObjectAnimator就能更加得心应手,因为从继承关系中我们可以知道ObjectAnimator是ValueAnimator的子类。

  • ObjectAnimator

ObjectAnimator对象应该是我们产生属性动画效果最常用的类了,ValueAnimator仅仅是对数值进行平滑的过渡变化,而如果我们想对一个对象的任意属性进行动画效果,就需要使用ObjectAnimator对象了,还是先举个简单栗子吧。

1.效果图

Alpha动画

2.具体代码

//ofFloat(需要实现动画的控件ID,需要实现的动画效果名称,变化的区间范围)
ObjectAnimator objAnim = bjectAnimator.ofFloat(et_Animator,"alpha",0f, 1f, 0f, 1f);
objAnim.setDuration(6000);
objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float currentValue = (float) animation.getAnimatedValue();
        StringBuilder sb = new StringBuilder();
        et_Animator.setText(sb.append(currentValue).toString());
    }
});
objAnim.start();

以上代码实现的效果是:在6000毫秒内,让EditText控件平滑的实现了从完全透明>完全不透明>完全透明>完全不透明的过渡效果;并且对整个动画执行过程设置了监听,时时把当前动画的AnimatorValue值显示到EditText控件上面。

根据设置参数的不同,使用ObjectAnimator对象还可以实现translationX(竖直平移),translationY(水平平移),rotation(旋转),scaleX(水平缩放),scaleY(竖直缩放)等其他效果。当然还可以使用AnimatorSet对象来产生组合动画效果。

  • 效果图
综合栗子
  • 具体代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_OperationAnimator;
    private EditText et_Animator;
    private RadioGroup radioGroup;
    private ObjectAnimator objAnim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_Animator = (EditText) findViewById(R.id.et_Animator);
        radioGroup = (RadioGroup) findViewById(R.id.radioGroup);
        btn_OperationAnimator = (Button) findViewById(R.id.btn_OperationAnimator);

        btn_OperationAnimator.setOnClickListener(this);

    }
    @Override
    public void onClick(View v) {

        int checkedId = radioGroup.getCheckedRadioButtonId();

        switch (checkedId) {

            case R.id.rb_value:
                ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);
                valueAnimator.setDuration(3000);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                valueAnimator.start();
                break;

            case R.id.rb_alpha:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"alpha",0f, 1f, 0f, 1f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_translationX:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"translationX",0f, 100f, 0f, 200f, 0f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_translationY:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"translationY",0f, 100f, 0f, 200f, 0f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_rotation:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"rotation",0f, 180f, 0f, 360f, 0f);
                objAnim.setDuration(6000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_scaleX:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"scaleX", 4f, 1f, 2f, 1f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_scaleY:
                objAnim = ObjectAnimator.ofFloat(et_Animator,"scaleY",1f, 5f, 2f, 1f);
                objAnim.setDuration(3000);
                objAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float currentValue = (float) animation.getAnimatedValue();
                        StringBuilder sb = new StringBuilder();
                        et_Animator.setText(sb.append(currentValue).toString());
                    }
                });
                objAnim.start();
                break;
            case R.id.rb_animatorSet:
                ObjectAnimator objAnim_alpha = ObjectAnimator.ofFloat(et_Animator,"alpha",0f, 1f, 0f, 1f);
                ObjectAnimator objAnim_translationX = ObjectAnimator.ofFloat(et_Animator,"translationX",0f, 100f, 0f, 200f, 0f);
                ObjectAnimator objAnim_translationY = ObjectAnimator.ofFloat(et_Animator,"translationY",0f, 100f, 0f, 200f, 0f);
                ObjectAnimator objAnim_rotation = ObjectAnimator.ofFloat(et_Animator,"rotation",0f, 180f, 0f, 360f, 0f);
                ObjectAnimator objAnim_scaleX = ObjectAnimator.ofFloat(et_Animator,"scaleX", 4f, 1f, 2f, 1f);
                ObjectAnimator objAnim_scaleY = ObjectAnimator.ofFloat(et_Animator,"scaleY",1f, 5f, 2f, 1f);
                AnimatorSet animSet = new AnimatorSet();
                animSet.play(objAnim_alpha)
                        .before(objAnim_rotation)    //在play动画之后执行
                        .after(objAnim_scaleX)       //在play动画之前执行
                        .after(objAnim_scaleY)
                        .with(objAnim_translationX)  //和play动画同时执行
                        .with(objAnim_translationY);
                animSet.setDuration(5000);
                animSet.start();
                break;
        }
    }
}

栗子很简单,算是做个自我巩固以及方便日后回顾,如果不小心帮到别人了那自然也是极好的。最后附上Demo下载

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

推荐阅读更多精彩内容