zoukankan      html  css  js  c++  java
  • Android动画攻略—帧动画、补间动画、属性动画

    前言

    • 动画时Android开发中使用频率比较高的功能。
    • 对Android提供的补间动画,帧动画以及属性动画做出归纳总结。

    目录

    image

    1. 帧动画

    帧动画总体实现比较简单,其实现本身是实现一个图片集的连续播放,从而达到动画的效果。

    实现帧动画就必须将大量图片资源加入到APK当中,从而增加APK的大小,但是却可以实现比较复杂的动画效果。

    帧动画使用比较简单的,具体操作过程如下

    1. 将图片集导入到对应目录下
    2. 在drawable文件夹下新建文件anim_chat. xml,的代码实现如下
    <?xml version="1.0" encoding="UTF-8"?>
    <animation-list android:oneshot="false"
        xmlns:android="http://schemas.android.com/apk/res/android">
        //duraction字段可以用来设置该图片播放时长,drawable用来设置要显示的图片
        <item android:duration="230" android:drawable="@drawable/ic_chat_recording1" />
        <item android:duration="230" android:drawable="@drawable/ic_chat_recording2" />
        <item android:duration="230" android:drawable="@drawable/ic_chat_recording3" />
        <item android:duration="230" android:drawable="@drawable/ic_chat_recording4" />
        <item android:duration="230" android:drawable="@drawable/ic_chat_recording5" />
        <item android:duration="230" android:drawable="@drawable/ic_chat_recording6" />
    </animation-list>
    
    1. 在布局文件activity_main.xml当中添加组件
    <Button
            android:id="@+id/frame_animation_test"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="帧动画测试" />
    
        <ImageView
            android:id="@+id/frame_animation_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_chat_recording1"
            android:layout_marginTop="20dp"/>
    
    1. 在activity当中加入java代码实现
    public class MainActivity extends AppCompatActivity {
    
        private Button frameButton;
        private ImageView frameImage;
        private AnimationDrawable frameAnimation;
        boolean isStart = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initFrame();
        }
    
        private void initFrame() {
            //初始化控件
            frameButton = (Button) findViewById(R.id.frame_animation_test);
            frameImage = (ImageView) findViewById(R.id.frame_animation_img);
            //给ImageView设置drawable
            frameImage.setImageResource(R.drawable.anim_chat_recording);
            //给动画资源赋值
            frameAnimation = (AnimationDrawable) frameImage.getDrawable();
            //给按钮添加点击事件用来控制动画
            frameButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //当isStart=false时表示动画没有在播放,点击按钮开始播放
                    if (!isStart) {
                        frameAnimation.start();
                        isStart = true;
                    } else {//当isStart=true时表示动画正在播放,点击按钮停止播放
                        frameAnimation.stop();
                        isStart = false;
                    }
                }
            });
        }
    }
    

    运行代码进行测试
    image

    帧动画也存在使用纯java代码的实现方式,但是在应用当中并不多见,有兴趣可以了解一下,这里不做介绍。

    2. 补间动画

    与按帧播放的帧动画不同,补间动画只需要定义初始和结束时的状态,中间的动画过程将由系统自动补齐。

    • 特点:

      • 补间动画作用于View,可以实现视觉上的动画效果,但是并没有真正对视图做出改变。
      • 使用简单,可以使用非常简单的方式实现动画效果。
      • 实现方式可以有jave代码实现和XML代码实现两种。
    • 分类:补间动画可分为四类

    jave xml 效果
    AlphaAnimation alph 渐变透明度动画效果
    ScaleAnimation scale 渐变尺寸伸缩动画效果
    TranslateAnimation1 translate 画面转换位置移动动画效果
    RotateAnimation rotate 画面转移旋转动画效果

    后文将对四种补间动画效果做具体说明。

    2.1 alph动画

    特有属性:

    • android:fromAlpha:动画开始时的透明度。
    • android:toAlpha:动画结束时的透明度。

    Java代码实现

    直接上代码

    1. 在activity_main.xml当中定义布局资源
    <ImageView
            android:id="@+id/alph_animation_img"
            android:layout_width="150dp"
            android:layout_height="200dp"
            android:src="@drawable/animation_test1"/>
    

    后续内容的动画效果基本针对图片涉及到的xml布局都基本类似,将不再进行说明。

    1. 在activity当中进行实现
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initAlphJava();
        }
    
        private void initAlphJava() {
            alphImage = (ImageView) findViewById(R.id.alph_animation_img);
            alphImage.setVisibility(View.VISIBLE);
            //构造参数中,第一个参数为动画开始时候透明度,第二个参数toAlpha为 动画结束时候透明度
            //对于fromAlpha和toAlpha,1.0代表完全不透明状态,0.0代表完全透明状态
            AlphaAnimation mAlpha = new AlphaAnimation(1.0f,0.0f);
            //设置动画播放时间,2000MS=2S
            mAlpha.setDuration(2000);
            //设置动画循环次数,-1为一直循环
            mAlpha.setRepeatCount(-1);
            //设置动画循环方式Animation.REVERSE为倒叙播放,Animation.RESTART为重复播放
            mAlpha.setRepeatMode(Animation.REVERSE);
            //alphImage开始播放动画
            alphImage.startAnimation(mAlpha);
        }
    

    动画效果为:由原图显示渐变为隐藏状态。
    image

    XML实现

    1. 在res目录中新建anim文件夹。
    2. 在anim目录中新建一个alph_anim.xml文件(注意文件名小写)。
    3. 在alph_anim.xml文件当中对动画进行定义。
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <!--透明度控制动画效果 alpha-->
        <!--fromAlpha 属性为动画起始时透明度 0.0表示完全透明 1.0表示完全不透明-->
        <!--toAlpha   属性为动画结束时透明度-->
        <!--duration  属性为动画持续时间-->
        <alpha
            android:duration="2000" //播放时间为2秒
            android:fromAlpha="1.0" //初始透明度为完全显示
            android:toAlpha="0.0"   //结束透明度为完全透明
            android:repeatCount= "-1" />    //重复播放次数为无限循环
    </set>
    

    fromAlpha和toAlpha为alph动画的特有属性,1.0代表完全不透明状态,0.0代表完全透明状态

    1. 在activity当中对动画资源信息引用。
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initAlphXml();
        }
    
        private void initAlphXml() {
            alphImage = (ImageView) findViewById(R.id.alph_animation_img);
            Animation animation = AnimationUtils.loadAnimation(this,R.anim.alph_anim);
            alphImage.startAnimation(animation);
        }
    

    运行程序,动画效果与上述java实现相同。
    补间动画中有一些公共属性,其说明如下:

    android:duration: 动画执行的时间,以毫秒为单位
    android:fillEnabled:true|false 动画结束时还原到开始动画前的状态
    android:fillBefore:true|false 动画结束后视图会停留在动画开始的状态,如果fillEnabled的值为true,它的值才有意义,否则没有意义。默认值是true。
    android:fillAfter:true|false 动画结束后是否保留这个动画的最后一帧的效果,它的设置不受fillEnabled的影响
    android:repeatMode:reverse|restart 重复类型,reverse:表示倒序回放,restart:表示重新放一遍,这个属性必须与repeatCount联合使用,因为它的前提是重复,即重复播放时的播放类型。
    android:repeatCount:动画重复的次数(注意是重复的次数),设定具体数值,也是可以是infinite,表示无限循环
    android:interpolator:设定的插值器,它主要用来为动画设置一些特殊的效果,比方说:加速运动、减速运动等等。

    2.2 scale动画

    特有属性:

    • android:fromXScale起始的X方向上相对自身的缩放比例,类型float
    • android:toXScale:结尾的X方向上相对自身的缩放比例,类型float
    • android:fromYScale:起始的Y方向上相对自身的缩放比例,类型float
    • android:toYScale:结尾的Y方向上相对自身的缩放比例,类型float
    • android:pivotX: 缩放起点X轴坐标,可以是数值、百分数、百分数p。
    • android:pivotY: 缩放起点Y轴坐标,同pivotX。

    java代码实现

    1. 在activity_main.xml当中定义布局资源
    2. 在java代码当中的代码实现如下:

    动画实现效果:以自身中心点为原点,缩放为原大小的两倍。
    image
    构造方法

    ScaleAnimation(float fromX, float toX, float fromY, float toY,
                int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
    

    参数取值介绍

    对于前四个参数:0.0表示收缩到没有 1.0表示正常无伸缩 值小于1.0表示收缩 值大于1.0表示放大
    pivotXType存在三种取值
    pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 + pivotXValue数值的点(y方向同理)

    pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 + 自身宽度乘上pivotXValue数值的值(y方向同理)

    pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 + 父控件宽度乘上pivotXValue数值的值 (y方向同理)

    例子中所选参数为从原图大小X轴和Y轴缩放到原大小的两倍,缩放参照点为以自身宽高比例的50%处,也就是中心点。

    XML实现

    1. 在res目录中新建anim文件夹。
    2. 在anim目录中新建一个scale_anim.xml文件(注意文件名小写)。
    3. 在scale_anim.xml文件当中对动画进行定义。
    <set xmlns:android="http://schemas.android.com/apk/res/android" >
        <scale
            android:duration="3000"  //设置播放时长为3秒
            android:fillAfter="false"   //设置不保存播放完毕之后的画面
            android:fromXScale="1.0"    //起始画面X轴缩放倍数
            android:fromYScale="1.0"    //起始画面Y轴缩放倍数
            android:interpolator="@android:anim/accelerate_decelerate_interpolator"  
            //使用accelerate_decelerate_interpolator加速--减速差值器
            android:pivotX="50%"    //X轴缩放原点为自身宽度的50%
            android:pivotY="50%"    //Y轴缩放原点为自身宽度的50%
            android:toXScale="2.0"  //结束画面X轴缩放倍数
            android:toYScale="2.0"  //结束画面Y轴缩放倍数
            android:repeatCount= "-1"   //动画循环次数为无限循环
            android:repeatMode="reverse"/>  //循环模式为倒播
    
    </set>
    

    pivotX与pivotY相同,有三种取值方式:

    • 取值为数值:当为数值时,表示在当前View的左上角,加上参数值即原点处,做为旋转点X坐标,单位为px。
    • 取值为百分数:如果是50%表示在当前控件的左上角加上自己宽度的50%做为旋转点X坐标。
    • 取值为百分数p:如果是50%p,那么就是表示在当前的左上角加上父控件宽度的50%做为旋转点X坐标。
    1. 在activity当中对动画资源信息引用。
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initScaleXML();
        }
    
        private void initAlphXml() {
            private void initScaleXML() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            Animation animation = AnimationUtils.loadAnimation(this,R.anim.scale_anim);
            mImage.startAnimation(animation);
        }
        }
    

    运行程序,动画效果与上述java实现相同。

    2.3 translate动画

    特有属性:

    • android:fromXDelta:起始点X轴坐标。
    • android:fromYDelta:起始点Y坐标。
    • android:toXDelta:结束点X坐标
    • android:toYDelta:结束点Y坐标

    java代码实现

    1. 在activity_main.xml当中定义布局资源
    2. 在java代码当中的代码实现如下:
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initTranslateJava();
        }
     
        private void initTranslateJava() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            TranslateAnimation translateAnimation = new TranslateAnimation(0,300,0,300);
            //fromXDelta:起始点X轴的坐标
            //toXDelta:终止点X轴的坐标
            //fromYDelta:起始点Y轴的坐标
            //toYDelta:终止点Y轴的坐标
            translateAnimation.setDuration(2000);
            translateAnimation.setRepeatMode(Animation.REVERSE);
            //Animation.INFINITE与-1取值相同,为无限重播
            translateAnimation.setRepeatCount(Animation.INFINITE);
            mImage.startAnimation(translateAnimation);
        }
    

    上述代码实现效果:View向右下角45°移动,最终坐标为原左上角坐标的X轴正方向300,Y轴正方向300.
    动画效果如下
    image
    对于构造方法

    public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
                int fromYType, float fromYValue, int toYType, float toYValue) {
    

    参数类型可大体分为两种: value和type

    Type参数取值介绍

    • fromXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理) 默认为这种方式。
    • fromXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
    • fromXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)

    Value取值介绍:

    • 取值为数值:当为数值时,表示在当前View的左上角,即原点处加上参数值,做为旋转点X坐标,单位为px。
    • 取值为百分数:如果是50%表示在当前控件的左上角加上自己宽度的50%做为原点X坐标。
    • 取值为百分数p:如果是50%p,那么就是表示在当前的左上角加上父控件宽度的50%做为原点X坐标。

    XML实现

    1. 在res目录中新建anim文件夹。
    2. 在anim目录中新建一个translate_anim.xml文件(注意文件名小写)。
    3. 在translate_anim.xml文件当中对动画进行定义。
    <set xmlns:android="http://schemas.android.com/apk/res/android">
    
        <translate
            android:duration="2000"
            android:fromXDelta="0"
            android:fromYDelta="0"
            android:toXDelta="300"
            android:toYDelta="300" />
    
    </set>
    

    参数值类型介绍

    整型值:
    fromXDelta 属性为动画起始时 X坐标上的位置
    toXDelta 属性为动画结束时 X坐标上的位置
    fromYDelta 属性为动画起始时 Y坐标上的位置
    toYDelta 属性为动画结束时 Y坐标上的位置

    注意:
    没有指定fromXType toXType fromYType toYType 时候, 默认是以自己为相对参照物
    长整型值:
    duration 属性为动画持续时间
    说明: 时间以毫秒为单位

    1. 在activity当中对动画资源信息引用。
      @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initTranslateXML();
        }
    
        private void initTranslateXML() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            Animation animation = AnimationUtils.loadAnimation(this,R.anim.translate_anim);
            mImage.startAnimation(animation);
        }
    

    运行程序,动画效果与上述java实现相同。

    2.4 Rotate动画

    特有属性:

    • android:fromDegrees:动画开始时旋转的角度位置,float类型,正值代表顺时针方向度数,负值代码逆时针方向度数
    • android:toDegrees: 动画结束时旋转到的角度位置,float类型,正值代表顺时针方向度数,负值代码逆时针方向度数
    • android:pivotX:旋转点X轴坐标,float类型,可以是数值、百分数、百分数p三种样式。
    • android:pivotY:旋转点Y轴坐标,取值及意义跟android:pivotX一样。

    Java实现

    1. 在activity_main.xml当中定义布局资源
    2. 在java代码当中的代码实现如下:
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initRotateJava();
        }
        private void initRotateJava() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            RotateAnimation rotateAnimation = new RotateAnimation(0,90,Animation.RELATIVE_TO_SELF,
    0.5f,Animation.RELATIVE_TO_SELF,0.5f);
    //        参数说明:
    //        fromDegrees :动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
    //        toDegrees :动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
    //        pivotXType:旋转轴点的x坐标的模式
    //        pivotXValue:旋转轴点x坐标的相对值
    //        pivotYType:旋转轴点的y坐标的模式
    //        pivotYValue:旋转轴点y坐标的相对值
            rotateAnimation.setRepeatMode(Animation.REVERSE);
    //        视频循环模式
            rotateAnimation.setDuration(2000);
    //        视频播放时长
            rotateAnimation.setRepeatCount(Animation.INFINITE);
    //        视频循环次数
            mImage.startAnimation(rotateAnimation);
    

    上述代码实现效果:以自身中心点为坐标,顺时针旋转90度。
    image
    参数取值介绍

    fromDegrees :动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
    toDegrees :动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)

    XML实现

    1. 在res目录中新建anim文件夹。
    2. 在anim目录中新建一个set_anim.xml文件(注意文件名小写)。
    3. 在set_anim.xml文件当中对动画进行定义。
    <set xmlns:android="http://schemas.android.com/apk/res/android">
     <rotate
            android:duration="3000"
            android:fromDegrees="0"
            android:interpolator="@android:anim/accelerate_decelerate_interpolator"
            android:pivotX="50%"
            android:pivotY="50%"
            android:toDegrees="90" />    
    </set>
    
    <!--
         rotate 旋转动画效果
           属性:interpolator 指定一个动画的插入器
                 在我试验过程中,使用android.res.anim中的资源时候发现
                 有三种动画插入器:
                    accelerate_decelerate_interpolator   加速-减速 动画插入器
                    accelerate_interpolator               加速-动画插入器
                    decelerate_interpolator               减速- 动画插入器
                 其他的属于特定的动画效果
                               
           浮点数型值:
                fromDegrees 属性为动画起始时物件的角度    
                toDegrees   属性为动画结束时物件旋转的角度 可以大于360度   
    
            
                说明:
                         当角度为负数——表示逆时针旋转
                         当角度为正数——表示顺时针旋转              
                         (负数from——to正数:顺时针旋转)   
                         (负数from——to负数:逆时针旋转) 
                         (正数from——to正数:顺时针旋转) 
                         (正数from——to负数:逆时针旋转)       
    
                pivotX     属性为动画相对于物件的X坐标的开始位置
                pivotY     属性为动画相对于物件的Y坐标的开始位置
                    
                说明:        以上两个属性值 从0%-100%中取值
                             50%为物件的X或Y方向坐标上的中点位置
    
            长整型值:
                duration  属性为动画持续时间
                说明:       时间以毫秒为单位
    
        -->
    
    1. 在activity当中对动画资源信息引用。
       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initRotateXML();
        }
    
       private void initRotateXML() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            Animation animation = AnimationUtils.loadAnimation(this, R.anim.rotate_anim);
            mImage.startAnimation(animation);
        }
    

    运行程序,动画效果与上述java实现相同。

    2.5 组合动画(set标签)

    使用set标签可以做到多种动画类型的组合效果具体步骤如下

    1. 在res目录中新建anim文件夹。
    2. 在anim目录中新建一个set_anim.xml文件(注意文件名小写)。
    3. 在set_anim.xml文件当中对动画进行定义。
    <set xmlns:android="http://schemas.android.com/apk/res/android">
     <alpha
            android:fromAlpha= "0.0"
            android:toAlpha= "1.0"
            android:duration= "3000" />
            //alph动画 实现效果:从透明状态渐变到显示状态
    
        <scale
            android:fromXScale= "0.0"
            android:toXScale= "1.0"
            android:fromYScale= "0.0"
            android:toYScale= "1.0"
            android:pivotX= "50%"
            android:pivotY= "50%"
            android:duration= "3000" />
            //以自身原点为坐标从0缩放原图大小
    
        <rotate
            android:fromDegrees= "0"
            android:toDegrees= "720"
            android:pivotX= "50%"
            android:pivotY= "50%"
            android:duration= "3000"/>
            //旋转720°
    
         <translate
            android:startOffset= "3000"
            android:fromXDelta= "0"
            android:fromYDelta= "0"
            android:toXDelta= "85"
            android:toYDelta= "0"
            android:duration= "1000" />
            //3000毫秒以后,水平方向移动85距离
    
        <alpha
            android:startOffset= "4000"
            android:fromAlpha= "1.0"
            android:toAlpha= "0.0"
            android:duration= "1000" />
            //4000毫秒以后图片渐变消失
    </set>
    
    1. activity当中对动画资源信息引用。
       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
           initSet();
        }
    
      private void initSet() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            Animation animation = AnimationUtils.loadAnimation(this, R.anim.set_anim);
            animation.setRepeatCount(-1);
            mImage.startAnimation(animation);
        }
    

    动画效果如下:

    image
    补间动画使用方便,但是其针对view进行操作的特性,以及只能实现视图上的动画而不能改变实际的view位置大小等缺点使其功能受到限制。下面将介绍功能更加强大的属性动画。

    3.属性动画

    优势:

    • 在Android3.0推出了属性动画。相比于属性动画,View动画一个非常大的缺陷就是其不具备交互性。当某个元素发生 View动画之后,其相应事件的位置依然在进行前的地方,所以View动画只能做到普通的动画效果,尽量避免交互操作。
    • View动画的优势也十分明显:效率比较高,使用也方便。
    • View动画只能实现比较单一的View的移动缩放等效果,效果比较单一,属性动画则是通过属性值变化来重新布局View,理论上可以实现任何动画效果。
      问题:
    • 在使用上不如View动画方便。

    3.1 原理解析

    属性动画有两个非常重要的类:ValueAnimator 类 和 ObjectAnimator 类

    • ValueAnimator:通过不断控制值的变化,再不断手动赋给对象的属性,从而实现动画效果。
    • ObjectAnimator:直接对对象的属性值进行改变操作,通过不断控制值的变化,再不断自动赋给对象的属性,从而实现动画效果。其实现继承了ValueAnimator。

    3.2 差值器和估值器

    • 插值器(Interpolator)前面我们在介绍补间动画时就有提到过,它主要用来为动画设置一些特殊的效果,比方说:加速运动、减速运动、动画结束的时候弹等等。主要有
    • accelerate_decelerate_interpolator 加速-减速 动画插入器
    • accelerate_interpolator 加速-动画插入器
    • decelerate_interpolator 减速- 动画插入器
    • 估值器(TypeEvaluator)决定值的具体变化数值
      我们先来看一下系统提供的估值器:
    public class FloatEvaluator implements TypeEvaluator<Number> {
        /**
         * @param fraction   The fraction from the starting to the ending values
         * @param startValue The start value; should be of type <code>float</code> or
         *                   <code>Float</code>
         * @param endValue   The end value; should be of type <code>float</code> or <code>Float</code>
         * @return A linear interpolation between the start and end values, given the
         *         <code>fraction</code> parameter.
         */
        public Float evaluate(float fraction, Number startValue, Number endValue) {
        <!--fraction 动画的完成度-->
        <!--startValue 动画的初始值-->
        <!--endValue 动画的结束值-->
            float startFloat = startValue.floatValue();
            <!--计算返回结果的公式为result = x0 + t * (v1 - v0)-->
            <!--x0=startValue-->
            <!--x1 = endValue-->
            <!--t = fraction-->
            return startFloat + fraction * (endValue.floatValue() - startFloat);
        }
    }
    

    这是由系统提供的操作float类型数据的估值器,可以根据我们提供的fraction、startValue以及endValue不断地产float数值对象返回给我们,我们可以根据返回的数据对我们的View进行更改。

    但是很多情况下我们需要操作的不只是float或者int类型等基本数据类型,所以我们就需要自定义ObjectEvaluator

    模仿FloatEvaluator,自定义ObjectEvaluator如下:

    // 实现TypeEvaluator接口
    public class ObjectEvaluator implements TypeEvaluator<Object>{  
    
    // 复写evaluate()
    // 在evaluate()里写入对象动画过渡的逻辑
        @Override
        public Object evaluate(float fraction, Object startValue, Object endValue) {  
    
            ... // 写入对象动画过渡的逻辑
            // 返回对象动画过渡的逻辑计算后的值
            return value;
        }
    

    后面会进行实例说明。再回过头来看我们的主角:ValueAnimator和ObjectAnimator类

    3.3 ObjectAnimator类

    本质原理: 通过不断控制值的变化,再不断自动赋给对象的属性,从而实现动画效果。
    先来看一个简单的例子:

     private void initObjectAnimationJava() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            ObjectAnimator alpha = ObjectAnimator.ofFloat(mImage, "alpha", 0f, 1f);
            alpha.setDuration(2000);//设置动画时间
            alpha.setInterpolator(new DecelerateInterpolator());//设置动画插入器,减速
            alpha.setRepeatCount(-1);//设置动画重复次数,这里-1代表无限
            alpha.setRepeatMode(ValueAnimator.REVERSE);//设置动画循环模式,注意这里是ValueAnimator.REVERSE而不是Animator.REVERSE
            alpha.start();//启动动画。
        }
    

    实现效果:图片由隐藏渐渐显示。

    动画对象定义完后的操作与View动画基本一致,最主要的区别在于构造方法,这里我使用了ObjectAnimator.ofFloat,ofFloat()方法主要有两个作用:设置参数以及返回ObjectAnimator对象,其方法如下:

    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
            ObjectAnimator anim = new ObjectAnimator(target, propertyName);
            anim.setFloatValues(values);
            return anim;
        }
        //1. 需要操作的对象,注意这里与补间动画不同,不止限于View对象,而可以是任意操作对象。
        //2. propertyName需要操作的属性,这个属性必须要有get和set方法才可以。
        //3. 这是一个可变参数,为后续的变化趋势。如设置两个参数v1,v2,则属性由v1变化到v2,如果设置的是v1,v2,v3,则由v1变化到v2再变化到v3以此类推。
    
    • 透明度:
    ObjectAnimator alpha = ObjectAnimator.ofFloat(text, "alpha", 0f, 1f);
    
    • 透明度由0.0变为1.0。

    伸缩X轴:

    ObjectAnimator scaleX = ObjectAnimator.ofFloat(text, "scaleX", 0f, 1f);
    ObjectAnimator scaleY = ObjectAnimator.ofFloat(text, "scaleY", 0f, 1f);
    

    X轴(Y轴)方向由0缩放到原图大小。

    • 移动:
    ObjectAnimator translationUp = ObjectAnimator.ofFloat(text, "Y",0f,300f, 0f);
    

    Y轴方向:0f-> -300f,向左;-300f-> 0f,向右。

    • 旋转
    ObjectAnimator animator = ObjectAnimator.ofFloat(text, "rotation", 0f, 360f, 0f);
    

    旋转动画:0f -> 360f ,顺时针; 360f -> 0f,逆时针。
    可以操作的动画属性propertyName的取值可以为:

    属性 作用 数值类型
    Alpha 控制View的透明度 float
    TranslationX 控制X方向的位移 float
    TranslationY 控制Y方向的位移 float
    ScaleX 控制X方向的缩放倍数 float
    ScaleY 控制Y方向的缩放倍数 float
    Rotation 控制以屏幕方向为轴的旋转度数 float
    RotationX 控制以X轴为轴的旋转度数 float
    RotationY 控制以Y轴为轴的旋转度数 float

    ObjectAnimation组合动画

    Java代码

    使用AnimatorSet完成组合动画播放。

    • anim1.with(anim2):anim1与anim2同时播放。
    • anim1.befor(anim2):在anim2之前播放anim1。
    • anim1.after(anim2):在anim2之后播放anim1。
    • anim1.after(long delay):delay时间之后播放anim1。
      具体使用代码如下:
    private void initObjectAnimationSetJava() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            AnimatorSet set = new AnimatorSet() ;
            //X轴旋转180°
            ObjectAnimator rotationX = ObjectAnimator .ofFloat(mImage, "rotationX", 0f, 180f);
            rotationX.setDuration(2000);
            //Y轴旋转180°
            ObjectAnimator rotationY = ObjectAnimator .ofFloat(mImage, "rotationY", 0f, 180f);
            rotationY.setDuration(2000);
            //X轴方向缩放到原图大小
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(mImage, "scaleX", 0f, 1f);
            scaleX.setDuration(2000);
            //Y轴方向缩放到原图大小
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(mImage, "scaleY", 1f, 0f);
            scaleY.setDuration(2000);
    
            set.play(rotationX).with(scaleX); //X轴旋转与X轴缩放动画同时进行
            set.play(rotationX).before(rotationY); //X轴旋转动画完成以后开始Y轴旋转
            set.play(rotationY).with(scaleY);//Y轴旋转动画与Y轴缩放同时开始
            set.start(); //开始播放动画
        }
    

    动画效果:X轴旋转与X轴缩放动画同时进行,结束之后Y轴旋转动画与Y轴缩放同时开始。实际效果如下:
    image

    XML实现

    set中的属性android:ordering:规定了这个set中的动画的执行顺序,包括:

    • together(默认):set中的动画同时执行
    • sequentially:set中的动画按顺序执行
        android:ordering="sequentially">
            <set android:ordering="together">
                <objectAnimator
                    android:duration="2000"
                    android:propertyName="rotationX"
                    android:repeatMode="reverse"
                    android:valueFrom="0"
                    android:valueTo="180" />
                <objectAnimator
                    android:duration="2000"
                    android:propertyName="scaleX"
                    android:repeatMode="reverse"
                    android:valueFrom="0.0"
                    android:valueTo="1.0" />
            </set>
    
            <set android:ordering="together">
                <objectAnimator
                    android:duration="2000"
                    android:propertyName="rotationY"
                    android:repeatMode="reverse"
                    android:valueFrom="0"
                    android:valueTo="180" />
                <objectAnimator
                    android:duration="2000"
                    android:propertyName="scaleY"
                    android:repeatMode="reverse"
                    android:valueFrom="1.0"
                    android:valueTo="0.0" />
            </set>
    </set>
    

    java代码:

    private void initObjectAnimationSetXML() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            Animator animator = AnimatorInflater.loadAnimator(this,R.animator.object_set_anim);
            animator.setTarget(mImage);
            animator.start();
        }
    

    实现效果与java代码一致。

    3.4 ValueAnimator类

    ValueAnimator类本质上是一种值得操作机制,想要实现动画,是需要开发者手动将这些值 赋给对象的属性值。
    ValueAnimator动画同样存在java和xml两种使用方式。建议使用java代码的方式,这里只介绍java方式,xml有兴趣的可以自己了解一下。

    ValueAnimator类常用的方法与ObjectAnimation类似,主要方法有

    • public static ValueAnimator ofInt(int... values)
    • public static ValueAnimator ofArgb(int... values)
    • public static ValueAnimator ofFloat(float... values)
    • public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
      前三种方法使用类似,后面的参数都表示变化趋势,即:设置两个参数v1,v2,则属性由v1变化到v2,如果设置的是v1,v2,v3,则由v1变化到v2再变化到v3以此类推。

    以ofFloat为例,使用方法如下

    private void initValueAnimationJava() {
            mImage = (ImageView) findViewById(R.id.animation_img);
            //步骤1. 定义ValueAnimator方法
            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f,1.0f);
            //获取原控件宽高
            final int width = mImage.getLayoutParams().width;
            final int height = mImage.getLayoutParams().height;
            //设置动画属性
            valueAnimator.setDuration(2000);
            valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
            valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            //步骤2. 添加AnimatorUpdateListener方法永联监听值得变化趋势
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animator) {
    
                    float currentValue = (Float) animator.getAnimatedValue();
                    // 获得每次变化后的属性值
    
                    // 步骤3:每次值变化时,将值手动赋值给对象的属性
                    // 即将每次变化后的值 赋 给按钮的宽度,这样就实现了按钮宽度属性的动态变化
    
                    mImage.getLayoutParams().width = (int) (width*currentValue);
                    mImage.getLayoutParams().height = (int) (height*currentValue);
    
                    // 步骤4:刷新视图,即重新绘制,从而实现动画效果
                    mImage.requestLayout();
    
                }
            });
            valueAnimator.start();
        }
    

    实际动画效果如下:
    image

    如果我们想实现一些比较负载的动画效果,而系统提供的估值器满足不来我们时怎么办,这时候我们之前提到的估值器TypeEvaluator就该登场了,通过方法public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values),设置估值器以后将对数据按照估值器进行计算并返回值,通过点赞动画例子了解实战应用。

    4. 属性动画实战--点赞动画

    不多说,直接上代码

    1. 首先是点赞动画的自定义PraiseLayout
    public class PraiseLayout extends RelativeLayout {
        //底部点赞按钮宽高
        private int dHeight;
        private int dWidth;
        //整个View空间宽高
        private int mHeight;
        private int mWidth;
        private LayoutParams layoutParams;
        private Random random = new Random();
        //图片资源数组
        private Drawable[] drawables;
    
        public PraiseLayout(Context context) {
            super(context);
            init();
        }
    
        public PraiseLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public PraiseLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            //初始化图片资源
            drawables = new Drawable[4];
            Drawable red = ContextCompat.getDrawable(getContext(), R.drawable.nemo_heart_red);
            Drawable blue = ContextCompat.getDrawable(getContext(), R.drawable.nemo_heart_blue);
            Drawable orange = ContextCompat.getDrawable(getContext(), R.drawable.nemo_heart_orange);
            Drawable pink = ContextCompat.getDrawable(getContext(), R.drawable.nemo_heart_pink);
    
            drawables[0] = red;
            drawables[1] = blue;
            drawables[2] = orange;
            drawables[3] = pink;
    
            dHeight = red.getIntrinsicHeight();
            dWidth = red.getIntrinsicWidth();
    
            layoutParams = new LayoutParams(dWidth, dHeight);
    
            //点赞动画开始于布局底部
            layoutParams.addRule(CENTER_HORIZONTAL, TRUE);
            layoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE);
        }
    
        //获取组件大小
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            mHeight = getMeasuredHeight();
            mWidth = getMeasuredWidth();
        }
        //点击动画事件
        public void addFavorWithoutBiz() {
            ImageView imageView = new ImageView(getContext());
            imageView.setImageDrawable(drawables[random.nextInt(4)]);
            imageView.setLayoutParams(layoutParams);
    
            addView(imageView);
            if (mWidth > 0 && dHeight > 0) {
                Animator set = getBezierValueAnimator(imageView);
                set.addListener(new AnimEndListener(imageView));
                set.start();
            }
    
        }
        //添加点赞动画
        private ValueAnimator getBezierValueAnimator(View target) {
            //点赞动画的估值器,参数为贝塞尔曲线两个中间点
            BezierEvaluator evaluator = new BezierEvaluator(getPointF(2), getPointF(1));
    
            //参数1:估值器对象  参数2、3:三重贝塞尔曲线的起点和终止点。
            ValueAnimator animator = ValueAnimator.ofObject(evaluator, new PointF((mWidth - dWidth) / 2, mHeight - dHeight - 20), new PointF(random.nextInt(getWidth()), 0));
            animator.addUpdateListener(new BezierListenr(target));
            animator.setTarget(target);
            animator.setDuration(3000);
            return animator;
        }
        private PointF getPointF(int scale) {
    
            PointF pointF = new PointF();
    
            pointF.x = random.nextInt((mWidth - 50));//减去50 是为了控制 x轴活动范围,看效果 随意~~
            //再Y轴上 为了确保第二个点 在第一个点之上,我把Y分成了上下两半 这样动画效果好一些  也可以用其他方法
            pointF.y = random.nextInt((mHeight - 150)) / scale;
            return pointF;
        }
    
    
        //终止动画监听
        class AnimEndListener extends AnimatorListenerAdapter {
            private View target;
            public AnimEndListener(View target) {
                this.target = target;
            }
    
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                removeView((target));
            }
        }
    
        //属性动画的监听类,当数值更新时触发
        class BezierListenr implements ValueAnimator.AnimatorUpdateListener {
            private View target;
    
            public BezierListenr(View target) {
                this.target = target;
            }
    
            @Override
    
            public void onAnimationUpdate(ValueAnimator animation) {
                //这里获取到贝塞尔曲线计算出来的的x y值 赋值给view 这样就能让爱心随着曲线走啦
                PointF pointF = (PointF) animation.getAnimatedValue();
                target.setX(pointF.x);
                target.setY(pointF.y);
                // alpha动画
                target.setAlpha(1 - animation.getAnimatedFraction());
            }
        }
    
    }
    
    1. 估值器BezierEvaluator的实现,这里使用三重贝塞尔曲线的公式计算。
    public class BezierEvaluator implements TypeEvaluator<PointF> {
    
        private PointF pointF1;
        private PointF pointF2;
    
        public BezierEvaluator(PointF pointF1, PointF pointF2) {
            this.pointF1 = pointF1;
            this.pointF2 = pointF2;
        }
    
        @Override
        public PointF evaluate(float time, PointF startValue, PointF endValue) {
            float timeLeft = 1.0f - time;
            PointF point = new PointF();//结果
    
            point.x = timeLeft * timeLeft * timeLeft * (startValue.x)
                    + 3 * timeLeft * timeLeft * time * (pointF1.x)
                    + 3 * timeLeft * time * time * (pointF2.x)
                    + time * time * time * (endValue.x);
    
            point.y = timeLeft * timeLeft * timeLeft * (startValue.y)
                    + 3 * timeLeft * timeLeft * time * (pointF1.y)
                    + 3 * timeLeft * time * time * (pointF2.y)
                    + time * time * time * (endValue.y);
    
            return point;
        }
    }
    
    1. 在xml布局文件中引入自定义布局
     <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <com.example.liubohua.myapplication.PraiseLayout
                android:id="@+id/animation_praise"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
            </com.example.liubohua.myapplication.PraiseLayout>
    
            <ImageView
                android:id="@+id/animation_praise_img"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/nemo_heart_blue"
                android:layout_gravity="center|bottom"/>
    
        </FrameLayout>
    
    
    1. 在java代码实现
     private void initPraise() {
            animation_praise_img = (ImageView) findViewById(R.id.animation_praise_img);
            animation_praise = (PraiseLayout) findViewById(R.id.animation_praise);
            animation_praise_img.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    animation_praise.addFavorWithoutBiz();
                }
            });
        }
    

    实现效果图:
    image
    尾声
    差不多Android常见的动画操作也就这些,通过简单的学习再加上庞大的脑洞就可以实现非常炫酷的效果,赶紧尝试一下吧。

    转自Android动画攻略—帧动画、补间动画、属性动画

  • 相关阅读:
    读《构建之法》8、9、10章有感
    评论
    复利计算器(4)——jQuery界面美化、自动补全
    送给搭档的“汉堡”
    MFC之TreeCtrl控件使用经验总结
    MFC绘制图片闪烁详解
    MFC 网络编程中::connect返回-1问题
    C++网络编程之select
    C++关于Condition Variable
    Mutex 和 Lock
  • 原文地址:https://www.cnblogs.com/sishuiliuyun/p/15110851.html
Copyright © 2011-2022 走看看