绘图篇——android属性动画

本文讲介绍android在3.0之后推出的一种新的动画机制,属性动画,对动画不了解的同学,可以先去看看绘图篇——android动画基础这篇文章。好了,现在我们进入正题。

    • 基本概念

        • android传统动画Animation与属性动画Animator的工作原理
        • Animator出现的原因
        • Animation的局限性
    • 属性动画ObjectAnimator
        • translationX
        • translationY
        • X和Y
        • rotation
        • 组合动画
    • 动画事件监听
    • 小栗子

基本概念

【android传统动画Animation与属性动画Animator的工作原理】

简单来说,传统动画就是不断的去调用系统的onDraw方法,去重新绘制组件,而属性动画则是通过调用属性的get和set方法重新设置控件的属性值,实现动画的效果

【Animator出现的原因】

既然已经存在了可以实现各种动画的方法了,为什么谷歌还要推出新的动画框架呢?为了解释这个问题,我们写一个小栗子。

首先是布局文件,很简单,就是一个imageView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"/>
</RelativeLayout>

接下来是java代码,我们找到这个view,为其添加点击事件,这里直接简单的Toast一下,表示view被点击;然后我们为这个view添加一个位移动画。代码如下:

package com.example.dell.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {
    private ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView= (ImageView) findViewById(R.id.image);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this,"ImageView",Toast.LENGTH_SHORT).show();
            }
        });
        //参数1:X开始位置 参数2:x结束的位置
        //参数3:y开始位置 参数4:y结束的位置
        TranslateAnimation animation =new TranslateAnimation(0f,500f,0f,0f);
        animation.setDuration(2000);
        //动画结束后应用动画
        animation.setFillAfter(true);
        imageView.startAnimation(animation);
    }

}

我们来看看演示效果:

可以看到,当我们进入到Activity当中后,动画就已经完成了,大家注意一下,这段代码其实是有问题的,因为我们只看到了动画的结果,而没看到动画的过程,这样一来,动画设置的就毫无意义。为什么会出现这种情况呢?这涉及到了Activity生命周期的内容,答案将在本节最后揭晓。

现在这时我先点的imageview完成动画后的位置(0f,100f)发现并没有弹出Toast,然后我们再去点击imageView的初始位置(0f,0f),有意思的事情发生了,Toast被弹出来了

那么这些说明了什么呢?

说明了我们在使用Animation的时候,虽然可以改变动画在界面上显示的位置,但是却不能改变点击事件所在的位置

到这里,传统Animation一个很大的局限性,它只是重绘了动画,改变了显示的位置,但是真正事件响应的位置,却没有发生任何改变。所以,Animation并不适合制作具有交互的动画效果。它只能用来完成一些显示性的效果

那么下面,我们就来列一下传统Animation的局限性

【Animation的局限性】

  • 第一点就是上面所说的内容,不在啰嗦了。
  • 因为Animation的工作原理,上面介绍了,是不断调用系统的onDraw()方法去绘制图像,那么必然十分耗费GPU,效率不高
  • Animation提供了位移、旋转、透明度、缩放这四种动画,虽然经过组合,可以创造出很多的动画效果,但是有时候依然无法制作出复杂好看的动画,即存在动画效果上的局限。

为解决以上的局限性,Google在android3.0后推出了属性动画。那么接下来我们就正式进入属性动画的内容。

在这之前,我们似乎还有一个小问题没有解决,那就是为什么上一个栗子的动画没有显示出来呢?

答案是,动画的启动写在了onCreate()方法当中,而在这之后,还有onStart(),onResume()方法,而用户真正看到界面的时候,onCreate方法早已经调用完毕了,此时如果动画的持续时间过短,那么用户看到界面时动画自然已经结束了。解决的办法也非常简单,只需要把动画的启动逻辑放在onResume() (活动准备好和用户进行交互的时候 )方法当中即可

补充:经过测试,发现在onResume()方法当中也会出现一些延时,这因为机器性能的问题,启动一个活动的时间长短不一,即便调用了onResume()方法,距离Activity的启动可能还有一段时间。但是这种写法肯定要比在onCreate()方法中启动动画要好一些。

属性动画——ObjectAnimator

【“translationX”】

现在我们在上面栗子的基础上增加一个button,将动画的启动逻辑放在里面,这样就能避免刚才动画显示的问题。

更改后的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"/>
    <Button
        android:id="@+id/bt_move"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="move"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

接下来是java代码

public class MainActivity extends Activity {
    private ImageView imageView;
    private TranslateAnimation animation;
    private Button bt_move;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView= (ImageView) findViewById(R.id.image);
        bt_move= (Button) findViewById(R.id.bt_move);
        //view的点击事件
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this,"ImageView",Toast.LENGTH_SHORT).show();
            }
        });
        //button的点击事件
        bt_move.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //使用ObjectAnimator
                //第一个参数是被操纵的view对象;第二个参数是被操作的属性
                //第三,第四个参数是可变长的数,表示熟悉所变化的范围
                ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(imageView,"translationX",0f,200f);
                //设置显示时长
                objectAnimator.setDuration(2000);
                //让动画开始
                objectAnimator.start();

            }
        });

    }

}

我们看一下效果

这里很明显,view的点击事件也发生了相应的变化。

【“translationY”】

想要改变Y上的属性,也很简单,代码如下:

ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(imageView,"translationY",0f,200f);
    //设置显示时长
   objectAnimator.setDuration(2000);
   //让动画开始
   objectAnimator.start();

看到这里,有的小伙伴可能会有疑问,到底那些属性是我们可以去设置的呢?其实只要是Google定义了的可以通过set和get去操纵的属性,我们都可以在属性动画中对其进行设置。

【“X和Y”】

前面已经讲了translationX和translationY,那么现在出现的X和Y又是什么东西呢?

translationX是指的是物体的偏移量,而X是指物体最后到达的一个绝对值,即具体移动到的坐标

【“rotation”】

旋转属性,旋转360度

ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(imageView,"rotation",0f,360f);
                //设置显示时长
                objectAnimator.setDuration(2000);
                //让动画开始
                objectAnimator.start();

以此类推,可以操作的属性还有很多,凡是可以通过get或者set设置的属性,都可以设置成属性动画。

【组合动画】

现在我们使用ObjectAnimator来进行多个动画效果的组合

  ObjectAnimator.ofFloat(imageView,"translationY",0f,100f).setDuration(1000).start();
                ObjectAnimator.ofFloat(imageView,"translationX",0f,100f).setDuration(1000).start();
                ObjectAnimator.ofFloat(imageView,"rotation",0f,360f).setDuration(1000).start();

效果图:

可以发现,这三个动画的效果是同时完成的,因为在调用start()方法之后,实际上是一个异步的过程。

实际上,我们还有更好的方法去实现这种组合效果,我们对上面的写法进行优化:

       PropertyValuesHolder p1=PropertyValuesHolder.ofFloat("translationY",0f,100f);
                PropertyValuesHolder p2=PropertyValuesHolder.ofFloat("translationX",0f,100f);
                PropertyValuesHolder p3=PropertyValuesHolder.ofFloat("rotation",0f,360f);
                //通过ObjectAnimator来调用ofPropertyValuesHolder()方法
                //第一个参数传递view,后续的参数为可变长的数组
                ObjectAnimator.ofPropertyValuesHolder(imageView,p1,p2,p3).setDuration(1000).start();

这里我们使用了一个PropertyValuesHolder的容器来容纳3个动画效果,然后在最后调用ObjectAnimator的ofPropertyValuesHolder()方法来加载之前定义的三个holder。那么这样写的好处是什么呢?Google在PropertyValuesHolder这个类中对动画进行了一些优化,这些优化使得我们在使用多个动画属性的时候能够更加有效率,更加节省系统资源。

在前面的动画基础当中,有一个动画集合的概念,那么在属性动画当中其实也有这么一个集合的概念。下面我们就用AnimatorSet来实现上述的效果。

代码如下:

AnimatorSet animatorSet=new AnimatorSet();
                //接下来将单个动画添加到AnimatorSet当中
                animatorSet.playTogether(animator1,animator2,animator3);
                animatorSet.setDuration(1000);
                animatorSet.start();

当然在AnimatorSet方法当中我们还有更多的选择去控制动画。从animatorSet.playTogether()这个方法的名字中就能看出,该方法是让所有的动画同时起效果,我们才看到了和刚才几种方法实现的同样的效果。这里google还提供了playSequentially()方法,该方法则是按顺序去播放动画。大家可以试一下。

我们还可以使用play()方法,这里我们实现view在X和Y轴上同时平移,结束之后再旋转360度。

代码如下:

                ObjectAnimator animator1= ObjectAnimator.ofFloat(imageView,"translationY",0f,100f);
                ObjectAnimator animator2= ObjectAnimator.ofFloat(imageView,"translationX",0f,100f);
                ObjectAnimator animator3= ObjectAnimator.ofFloat(imageView,"rotation",0f,360f);

                AnimatorSet animatorSet=new AnimatorSet();
                //animator1和Animator2同时播放
                animatorSet.play(animator1).with(animator2);
                //Animator3在Animator1或者Animator2结束后播放
                animatorSet.play(animator3).after(animator2);
                animatorSet.setDuration(1000);
                animatorSet.start();

这样一来就实现了对每个动画更加细致的控制。通过play(),with(),after(),before()方法,我们就能做到对一个属性集合的详细的顺序控制。这种方式,也是属性动画框架中使用最多的一种配和。

接下来总结一下

(1)通过ObjectAnimator进行更精细的控制,只控制一个对象的一个属性。

(2)同时多个ObjectAnimator组合到AnimatorSet当中,可以形成一个完整的动画效果。

(3)而且AnimatorSet可以自动驱动,可以去调用play(),with(),after(),before,playTogether(),playSequentially()实现更为丰富的动画效果。

动画事件监听

顾名思义,和一般的点击事件差不多,我们为ObjectAnimator设置监听事件,以满足实际开发当中的需求。

       bt_move.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

  ………………

                ObjectAnimator bt_animator=ObjectAnimator.ofFloat(view,"alpha",0f,1f);
                bt_animator.setDuration(1000);
                  bt_animator.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animator) {
                    //动画开始前调用
                    }

                    @Override
                    public void onAnimationEnd(Animator animator) {
                    //动画结束后调用,为简单起见,这里我们只简单的提示一行字
                        Toast.makeText(MainActivity.this,"anim is end",Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onAnimationCancel(Animator animator) {
                    //动画被取消后
                    }

                    @Override
                    public void onAnimationRepeat(Animator animator) {
                    //动画重复时调用
                    }
                });
                bt_animator.start();

…………
            }
        });

我们在上一个栗子的基础上为我们的Button添加一个alpha动画,让其从透明变成不透明,然后在这个动画里面加入了一个监听事件,在监听事件当中,我们看到了4个需要重写的方法,通过这4个方法,我们就可以监听动画在不同事件段所需要完成的操作。

演示效果:

现在我们在考虑一种情况,那就是如果我们并不需要重写那么多的方法该怎么办呢?这时可以使用android系统提供的一个更方便的接口AnimatorListenerAdapter(),大家可以发现,系统帮我们实现了很多方法,这里我们只需要添加需要重写的方法即可

代码如下:

 bt_animator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        Toast.makeText(MainActivity.this,"anim is end",Toast.LENGTH_SHORT).show();
                    }
                });

这样我们就实现了对某一个事件的监听,而不需要写出所有的事件。

总结一下

看到出来动画监听事件还是比较简单的,我们只需要调用ObjectAnimator的addListener()方法,就能为我们的Animator增加一个监听事件,接着我们可以通过 AnimatorListenerAdapter这个类,去有选择的去选取我们需要监听的事件。

小栗子

这里的栗子来自于慕课网的课程,有兴趣的小伙伴可以去看看。

在开始写这个栗子之前,请各位小伙伴去下载一下素材,其中a.png的尺寸有点问题,改为58*58即可

演示效果:

接下来让让我们看看布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/view_b"
        android:src="@drawable/b"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/view_c"
        android:src="@drawable/c"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/view_d"
        android:src="@drawable/d"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/view_e"
        android:src="@drawable/e"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/view_f"
        android:src="@drawable/f"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/view_g"
        android:src="@drawable/g"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/view_h"
        android:src="@drawable/h"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/view_a"
        android:src="@drawable/a"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</FrameLayout>

里面定义了我们需要用到的imageView

接下来是比较关键的java代码:

public class MainActivity extends Activity implements View.OnClickListener {
    //定义每一个图片资源
    private int[] res = {R.id.view_a, R.id.view_b, R.id.view_c, R.id.view_d, R.id.view_e, R.id.view_f, R.id.view_g,
            R.id.view_h};
    //存储viewd的list集合
    private List<ImageView> imageViewList = new ArrayList<>();
    //设置一个flag来标示列表是否展开
    private boolean flag = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        for (int i = 0; i < res.length; i++) {
            //实例化imageView
            ImageView imageView = (ImageView) findViewById(res[i]);
            //添加点击事件
            imageView.setOnClickListener(this);
            //将每一个ImageView添加到list当中
            imageViewList.add(imageView);
        }

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.view_a:
                //点击最上面的图片,显示展开动画
                //动画方法
                if (flag) {
                    //列表未展开
                    startAnimation();
                    flag = false;
                } else {
                    //列表已展开
                    stopAnimation();
                    flag = true;
                }
                break;
            default:
                //其他按钮
                Toast.makeText(this, "clicked", Toast.LENGTH_SHORT).show();
                break;
        }
    }

    private void stopAnimation() {
        for (int i = 1; i < res.length - 1; i++) {
            ObjectAnimator animator = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 200 * i, 0);
            animator.setDuration(500);
            //设置插值器
            animator.setInterpolator(new AnticipateInterpolator());
            animator.start();
        }
    }

    private void startAnimation() {
        for (int i = 1; i < res.length - 1; i++) {
            ObjectAnimator animator = ObjectAnimator.ofFloat(imageViewList.get(i), "translationY", 0f, 200 * i);
            animator.setDuration(500);
            //设置延迟,让动画更加丰富
            animator.setStartDelay(i * 100);
            //设置插值器
            animator.setInterpolator(new OvershootInterpolator());
            animator.start();
        }
    }
}

上述代码当中有非常详细的注释,这里就不多解释了。为了让动画效果更棒,有时我们还可以为动画添加差插器(interpolator)。android内置的插值器有如下

- Accelerate

- Decelerate

- Accelerate/Decelerate、

- Overshoot

- Bounce

通过插值器,我们让某一属性在数值上的变化时,可以拥有不同的加速曲线,进而让我们的动画更加丰富。

时间: 2024-08-04 07:36:31

绘图篇——android属性动画的相关文章

Android 属性动画(Property Animation) 完全解析 (上)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38067475 1.概述 Android提供了几种动画类型:View Animation .Drawable Animation .Property Animation .View Animation相当简单,不过只能支持简单的缩放.平移.旋转.透明度基本的动画,且有一定的局限性.比如:你希望View有一个颜色的切换动画:你希望可以使用3D旋转动画:你希望当动画停止时,View的

Android 属性动画(Property Animation) 完全解析 (下)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38092093 上一篇Android 属性动画(Property Animation) 完全解析 (上)已经基本展示了属性动画的核心用法: ObjectAnimator实现动画,ValueAnimator实现动画,AnimatorSet的使用等~ 当然了属性动画还有一部分的知识点,也能做出很不错的效果,将在本篇博客为您展示~ 1.如何使用xml文件来创建属性动画 大家肯定都清楚,

通过AnimationSet 同步或一部播放多个动画 Android 属性动画(Property Animation) 完全解析 (下)

AnimationSet提供了一个把多个动画组合成一个组合的机制,并可设置组中动画的时序关系,如同时播放,顺序播放等. 以下例子同时应用5个动画: 播放anim1: 同时播放anim2,anim3,anim4: 播放anim5. AnimatorSet bouncer = new AnimatorSet(); bouncer.play(anim1).before(anim2); bouncer.play(anim2).with(anim3); bouncer.play(anim2).with(a

【转】android 属性动画之 ObjectAnimator

原文网址:http://blog.csdn.net/feiduclear_up/article/details/39255083 前面一篇博客讲解了 android 简单动画之 animtion,这里来讲解一下android 3.0之后添加的一些动画   animator 中的 ObjectAnimator . 属性动画概念: 所谓属性动画:改变一切能改变的对象的属性值,不同于补间动画:只能改变 alpha,scale,rotate,translate.听着有点抽象,举例子说明 补间动画能实现的

Android属性动画初步学习笔记

近期学习Android属性动画和VetcorDrawable实现属性动画,以此记录一下学习笔记. 首先是属性动画,小白没截过动态图,方三张静态图吧       效果是点击红色图片,7个选项以属性动画的方式弹出并旋转,最后成一个1/4圆弧排列,再次点击则收回到红色原点下. 布局文件很简单,就是一个RelativeLayout下八个ImageView: 1 <?xml version="1.0" encoding="utf-8"?> 2 <Linear

Android属性动画之ValueAnimator的介绍

之前两篇博客,介绍的是ObjectAnimator作用与某一个控件的某一个属性.但我们的ValueAnimator它本身并不会作用与任何一个属性,它本身也不会提供任何一种动画.它简单的来说,就是一个数值发生器,它可以产生你想要的各种数值.其实,在Android属性动画中,如何产生每一步具体实现动画效果,都是通过ValueAnimator计算出来的. 比如我们现在要实现一个从0~100的位移动画,ValueAnimator会根据动画持续的总时间产生一个0~1时间因子,有了这样一个时间因子.通过相应

Android属性动画ValueAnimator源码简单分析

Android开发的过程中经常要用到属性动画,经常都是网上扒下来看下怎么用,但是经常不知道为什么要这么用,手一哆嗦一不小心就点到源码里面去了.我们就来看看Android属性动画ValueAnimator类源码的简单实现,从而对ValueAnimator类有个大概的了解. 在Android开发过程中做动画效果的时候用到ValueAnimator的时候最简单的方法我们是这么干的 // ValueAnimator ValueAnimator valueAnimator = ValueAnimator.

Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/44171115 大家好,欢迎继续回到Android属性动画完全解析.在上一篇文章当中我们学习了属性动画的一些进阶技巧,包括ValueAnimator和ObjectAnimator的高级用法,那么除了这些之外,当然还有一些其它的高级技巧在等着我们学习,因此本篇文章就对整个属性动画完全解析系列收个尾,来学习一下剩下的非常重要的高级技巧. 另外,本篇文章中使用的代码是建立在上篇文章基础之

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

Android属性动画完全解析(上) 转载:http://blog.csdn.net/guolin_blog/article/details/43536355 在手机上去实现一些动画效果算是件比较炫酷的事情,因此Android系统在一开始的时候就给我们提供了两种实现动画效果的方式,逐帧动画(frame-by-frame animation)和补间动画(tweened animation).逐帧动画的工作原理很简单,其实就是将一个完整的动画拆分成一张张单独的图片,然后再将它们连贯起来进行播放,类似