第七章 Android 动画机制与使用技巧

作者: YoungerDev | 来源:发表于2018-03-12 08:08 被阅读84次

    Android 群英传笔记
    第一章Android体系与系统架构
    第二章 Android开发工具及技巧
    第三章 Android控件架构与事件拦截机制
    第四章 ListView 使用技巧
    第五章 Android Scroll 分析
    第六章 Android 绘图机制与屏幕适配
    第七章 Android 动画机制与使用技巧
    第八章 Activity与Activity调用栈分析
    第九章 Android 系统信息与安全机制
    第十章 Android性能优化
    本文出自:
    http://www.jianshu.com/u/a1251e598483

    Android动画效果一直是人家中十分重要的一部分,从早期的Android版本中,由于动画机制和绘图机制的不健全,Android的人机交互备受诟病,Android从4.X开始,特别是5.X,动画越来越完善了,Google也开始重视这一方面了,我们本章学习的主要内容有

    • Android视图动画‘
    • Android属性动画
    • Android动画实例
    Android View动画框架

    Animation动画框架定义了透明度,旋转,缩放个移动等几种动画,而且控制了整个的View,实现原理是每次绘制视图的时候View所在的ViewGroup中drawChild函数获取该View的Animation的Transformation值,然后调用了canvas.concat方法,通过矩阵运算完成动画帧,如果动画没有完成则继续调用invalidate()方法启动下回绘制来驱动动画,从而完成整个动画的绘制

    视图动画使用简单,效果丰富,它提供了AlphaAnimation,RotateAnimatio,TranslateAnimation,ScaleAnimation四种动画方式,并提供了Animationset动画集合,混合使用多种动画,在Android3.0之前,视图动画一家独大,但随着Android3.0之后属性动画框架的推出它的风光就大不如前了。相比属性动画,视图动画的一个非常大的缺陷就是不具备交互性,当某个元件发生视图动画后,其响应事件的位置还依然在动画前的地方,所以视图动画只能做普通的显示效果,避免交互的发生,但是它的优点也非常明显,即效率比较高且使用方便。视图动画使用非常简单, 不仅可以通过XML文件来描述一个动画过程,同样也可以使用代码来控制整 个动画过程

    视图动画

    下面这个实例就列举了一些简单的视图动画使用方法

    • 透明动画

    为视图增加透明度的变换动画

    AlphaAnimation al = new AlphaAnimation(0,1);
    al.setDuration(2000);
    alpha.startAnimation(al);
    
    • 旋转动画
    RotateAnimation ro = new RotateAnimation(0,300,100,100);
     ro.setDuration(2000);
     rotate.setAnimation(ro)
    
    • 平移动画
    TranslateAnimation tr = new TranslateAnimation(0,200,0,300);
    tr.setDuration(2000);
    translate.setAnimation(tr);
    
    • 缩放动画
     ScaleAnimation sc = new ScaleAnimation(0,2,0,2);
     sc.setDuration(2000);
     scale.setAnimation(sc);
    
    • 动画集合
    AnimationSet setAnimation = new AnimationSet(true);
    setAnimation.setDuration(2000);
    
    AlphaAnimation als = new AlphaAnimation(0,1);
    als.setDuration(2000);
    setAnimation.addAnimation(als);
    
    RotateAnimation ros = new RotateAnimation(0,300,100,100);
    ros.setDuration(2000);
    setAnimation.addAnimation(ros);
    
    set.startAnimation(setAnimation);
    
    

    当然,有动画,就有监听,我们来监听一下动画,以透明动画为例

                    AlphaAnimation al = new AlphaAnimation(0,1);
                    al.setDuration(2000);
                    alpha.startAnimation(al);
    
                    al.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                            Log.i("Animation","开始");
                        }
    
                        @Override
                        public void onAnimationEnd(Animation animation) {
                            Log.i("Animation","结束");
                            Toast.makeText(MainActivity.this,"动画结束",Toast.LENGTH_LONG).show();
                        }
    
                        @Override
                        public void onAnimationRepeat(Animation animation) {
    
                        }
                    });
    

    Android属性动画分析

    属性动画在Animator框架里的,用的最多的也就是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator进行更精细化的控制,只控制一个对象的属性,而使用多个ObjectAnimator组合到AnimatorSet形成一个动画,而且ObjectAnimator能够自动驱动,单证各种好处,我们来看一下

    • ObjectAnimator

    ObjectAnimator是属性动画框架中最重要的实行类,创建一个ObjectAnimator只需通过他 的静态工厂类直接返回一个ObjectAnimator对象,参数包括一个对象和对象的属性名字,但这 个属性必须有get和对函数,内部会通过Java反射机制来调用set函数修改对象属性值.同样 你也可以调用setIn设置相应的差值器。 下面这个小例子就完成了一个非常简单的平 移动画,

    在前面的讲解中说到,以前的动画框架所产生的动画,并不能改变事件响应的位置,它只是单纯地修改了显示.如果使用旧的视图动画产生上面的效果,那么按钮的实际点击有效区依然在原来的地方,点击移动后的地方是不会有点击事件发生的.而属性动圆则不同,由于它真实地改变了一个View的属性,所以事件响应的区域也同样发生了改变,这时候点击移动后的按钮, 就会响应点击事件了

    让我们来看看这个简单的平移动画是如何实现的.麻雀虽小五脏俱全,这个简单的例子基本上就涵盖了ObjectAnimator的所有知识

    ObjectAnimator ob = ObjectAnimator.ofFloat(object,"translationX",300);
                   ob.setDuration(2000);
                   ob.start();
    

    通过ObjectAnimator的静态工厂方法,创建个ObjectAnimator对象,第一个参数自然是要操纵的view,第二个参数则是要操纵的属性 而最后个参数是一个可变数组参数,需要传递进去该属性变化的一个取值过程,

    不过,在使用ObjectAnimator的时候,有一点是非常重要的,就是操纵的set,get方法,不然ObjectAnimator是无效的,下面我们具体举一些值

    • translationX和 translationY:这两个属性作为一种增量来控制着View对象从它布局容器的左上角坐标便宜的位置。

    • rotation、rotationX和rotationY:这三个属性控制View对象围绕支点进行2D和3D旋转

    • scaleX和scaleY. 这两个属性控制着View对象围绕它的支点进行2D缩放。

    • pivotX和pivotY:这两个属性控制着view对象的支点位置,围绕这个支点进行旋转和缩放变换处理,默认情况下,该支点的位置就是View对象的中心点。

    • x和y这是两个简单实用的属性,它描述了View对象在它的容器中的最终位置,它是最初的左上角坐标和 translationX和 translationY值的累计和

    • alpha:它表示View对象的alpha透明度,默认值是1(不透明),0代表完全透明(不可见)

    特别注意,引号里面的单词一定要写正确,不然,看不到效果的
    由以上可知,视图动画所实现的动画效果,在这里基本都已经包含了,那么如果一个属性没有get,set方法,属性动画是不是就束手无策了答案当然是否定的,google在应用层提供了两种方案来解决这个问题, 一个是通过自定义一个属性类或者包装类,来间接地给这个属性增加get方法:或者通过ValusAnimator来实现,ValusAnimator在后面的内容中会讲到, 这里先来看看如何使用包装类的方法给一个属性增加set,get方法:

    private static class WrapperView {
    
            private View mTarget;
    
            public WrapperView(View target) {
                mTarget = target;
            }
    
            public int getWidth() {
                return mTarget.getLayoutParams().width;
            }
    
            public void setWidth(int width) {
                mTarget.getLayoutParams().width = width;
                mTarget.requestLayout();
            }
        }
    

    通过上面的代码就给一个属性包装上了一层,并且提供set,get方法,使用时操作就行了

    WrapperView vi = new WrapperView(alpha);
    ObjectAnimator.ofInt(vi,"width",500).setDuration(2000).start();
    
    • PropertyValuesHolder

    这个类类似视图动画中的AnimationSet,就是把动画给组合起来,在属性动画中,如果针对一个对象的多个属性,就同时需要多个动画了,可以使用PropertyValuesHolder,来实现,比如需要在平移的过程中,同时改变x,y的缩放,可以这样实现

     PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX",300f);
                    PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX",1f,0,1f);
                    PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY",1f,0,1f);
                    ObjectAnimator.ofPropertyValuesHolder(button3,pvh1,pvh2,pvh3).setDuration(1000).start();
    
    • ValueAnimator

    ValueAnimator这个属性在动画当中有很大的地位,虽然不像ObjectAnimator那样,耀眼,但是他确实属性动画的核心所在,ObjectAnimator也是继承自他

    public final class ObjectAnimator extends ValueAnimator
    

    ValueAnimator本身不提供任何动画,他更像是一个数值发生器,用来产生一定具有规律的数字,从而让调用者控制动画的整个过程,我们举个例子来说明

                    ValueAnimator va = ValueAnimator.ofFloat(0,100);
                    //除了 可以ofFloat 也可以ofInt 产生整数,可以在里面处理自己想要的动画效果
                    va.setTarget(value);
                    va.setDuration(2000).start();
                    va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            float values = (float) animation.getAnimatedValue();
                            Log.i("数值",values+"");
                        }
                    });
    
    • 动画事件的监听

    一个完整的动画是具有,start,repeat,end,cancel四个过程的,通过Android的接口,我们很容易监听到这几个事件

                 ob.addListener(new Animator.AnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {
    
                        }
    
                        @Override
                        public void onAnimationEnd(Animator animation) {
    
                        }
    
                        @Override
                        public void onAnimationCancel(Animator animation) {
    
                        }
    
                        @Override
                        public void onAnimationRepeat(Animator animation) {
    
                        }
                    });
    

    当然,大部分的场景吗,我们只关心动画结束,所以,Android也提供了一个AnimatorLisistenerAdapter来让你自己选择监听事件

            ob.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                        }
                    });
    
    • AnimatorSet

    对于一个属性同时作用在一个view上,前面已经有一个PropertyValuesHolder了,而AnimatorSet不仅能实现,而且能更精准的控制顺序,同样是实现PropertyValuesHolder的动画,AnimatorSet是这样实现的

            ObjectAnimator animator1 = ObjectAnimator.ofFloat(alpha, "translationX", 300f);
            ObjectAnimator animator2 = ObjectAnimator.ofFloat(alpha, "scaleX", 1f, 0, 1f);
            ObjectAnimator animator3 = ObjectAnimator.ofFloat(alpha, "scaleY", 1f, 0, 1f);
            AnimatorSet set = new AnimatorSet();
            set.setDuration(2000);
            set.playTogether(animator1,animator2,animator3);
            set.start();
    

    在属性动画中,AnimatorSet正是通过playTogether等方法控制多个动画协同工作,从而控制播放顺序的

    • 在XML中定义动画

    属性动画同样的可以定义在xml中
    首先在 res 文件夹下建一个animator 文件夹 在这个文件夹中创建一个animator 类型的xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:propertyName="scaleX"
        android:valueFrom="1.0"
        android:valueTo="2.0"
        android:valueType="floatType"
        >
    </objectAnimator>
    

    在代码中引用

    Animator animator = AnimatorInflater.loadAnimator(AnimationActivity.this,R.animator.scale);
                    animator.setTarget(button4);
                    animator.start();
    
    • View的animate方法

    在Android3.0,Google给view增加了animate方法直接来驱动属性动画,代码如下,我们可以发现,其实animate就是属性动画的一种缩写

     button4.animate().alpha(0).y(300).setDuration(2000).withStartAction(new Runnable() {
                        @Override
                        public void run() {
    
                        }
                    }).withEndAction(new Runnable() {
                        @Override
                        public void run() {
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
    
                                }
                            });
                        }
                    }).start();
    
    Android 布局动画

    所谓的布局动画,就是作用在ViewGruop中给View添加的过渡效果,最简单的方法是在xml中打开

    android:animateLayoutChanges="true"
    

    通过以上代码设置, 当ViewGroup 添加View 时, 子View 会呈现逐渐显示的过渡效果, 不过这都是Android自带的效果,我们还可以通过LayoutAnimationController来定义

       ll = (LinearLayout) findViewById(R.id.ll);
            //设置过渡动画
            ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1);
            sa.setDuration(2000);
            LayoutAnimationController lc = new LayoutAnimationController(sa, 0.5f);
            lc.setOrder(LayoutAnimationController.ORDER_NORMAL);
            //设置布局动画
            ll.setLayoutAnimation(lc);
    

    LayoutAnimationController的第一个参数 是需要作用的动画,第二个参数,是每个子View显示的delay 时间,当delay 时间不为0时,可以设置子View 显示的顺序

    • 1.LayoutAnimationController.ORDER_NORMAL 顺序
    • 2.LayoutAnimationController.ORDER_RANDOM 随机
    • 3.LayoutAnimationController.ORDER_REVEESE 反序
    • Interpolators(插值器)

    插值器在动画中是一个非常重要的概念,我们通过插值器可以定义动画变换速率,这一点非常类似物理中的加速度,其作用主要是目标变化对应的变化,同样的一个动画变换起始值,在不同的插值器的作用下,每个单位时间内所达到的变换值都是不一样的,例如一个平移动画,如果使用线性插值器,那么在持续时间内单位时间所移动的距离都是一样的,如果使用加速度插值器,那么单位时间内所移动的速度越来越快,大家如果把插值器的概念理解为一个人进行万米长跑,规定一个小时到达,有的人怕时间来不及一开始就加速跑但是到后面速度越来越慢,而有的人开始节省体力,所以开始跑的比较慢,后来越跑越快直到终点,不管怎么跑,最终他们的都是在规定的时间到达终点,唯一不同的是他们的跑的速度不同,通过这个例子,我们可以很好的理解插值器的概念

    • 自定义动画

    创建自定义动画很简单,只需要实现applyTransformation的逻辑就可以,不过通常情况下,我们还要覆盖父类的initialize方法来完成一些初始化工作,

    @Override
       protected void applyTransformation(float interpolatedTime, Transformation t) {
           super.applyTransformation(interpolatedTime, t);
       }
    

    第一个参数interpolatedTime是前面说的插值器的时间因子,这个因子是由动画当前完成的百分比和当前时间对应的差值计算的,取值范围在0-1.0,第二个参数就非常简单了,她是矩阵的封装类,一般使用这个类获取当前的矩阵对象,代码如下

     Matrix matrix = t.getMatrix();
    

    通过改变获得的matrix 对象,可以将动画效果实现,而对于matrix 的变换操作,基本上可以实现任何效果

    @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
    
            Matrix matrix = t.getMatrix();
            matrix.preScale(1, 1 - interpolatedTime, width,height);
            super.applyTransformation(interpolatedTime, t);
        }
    

    当然我们可以设置更加精准的插值器,,从而对不同的过程采用不同的动画效果,模拟的更加逼真

    接下来我们结合矩阵,并且使用Canmera来实现一个3D的效果,要注意的是,这里所指的Camera不是相机,而是这个类,他封装了openGl的3D动画,我们继续用代码来实现

    @Override
        public void initialize(int width, int height, int parentWidth, int parentHeight) {
    
            super.initialize(width, height, parentWidth, parentHeight);
            setDuration(2000);
            setFillAfter(true);
            setInterpolator(new BounceInterpolator());
            w = width / 2;
            h = height / 2;
    
        }
    
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
    
            Matrix matrix = t.getMatrix();
        //  matrix.preScale(1, 1 - interpolatedTime, w,h);
            mCamera.save();
            //设置旋转角度
            mCamera.rotate(0, 180, 360);
            mCamera.restore();
            //通过pre方法设置矩形作用前的偏移量来改变旋转中心
            matrix.preTranslate(w, h);
            matrix.postTranslate(-w, -h);
    
            super.applyTransformation(interpolatedTime, t);
    
        }
    

    通过以上的方法,就可以实现了

    Android SVG

    • 可伸缩矢量图形
    • 定义用于网络的基于矢量的图形
    • 使用xml格式定义图形
    • 图片在放大或者改变尺寸的情况下其图形质量不会有所损失
    • 万维网联盟的标准
    • 与诸多DOM和XSL之类的W3C标准是一个整体

    SVG在web上应用非常广泛,在Android5.X之前的Android版本上,大家可以通过一些第三方库在Android中使用SVG,而在Android5.X后,Android中添加了对< path >标签的支持,从而让开发者可以使用SVG来创建更加丰富的动画效果,那么SVG对传统的Bitmap,究竟有什么好处呢?bitmap通过每个像素点上存储色彩信息来表达图像,而SVG是一个绘图标准,与之相对,最大的优势是SVG放大不会失真,而且bitmap需要不同分辨率适配,SVG不需要

    < path >标签

    使用< path >标签来创建SVG,就是用指令的方式来控制一支画笔,列入,移动画笔来到某一个坐标位置,画一条线,画一条曲线,结束,< path >标签所支持的指令大致有一下几种

    M = moveto(M X,Y):将画笔移动到指定的坐标位置,但未发生绘制

    L = lineto(L X,Y):画直线到指定的位置

    H = horizontal lineto( H X):画水平线到指定的X坐标位置

    V = vertical lineto(V Y ):画垂直线到指定的Y坐标

    C = curveto(C ,X1,Y1,X2,Y2,ENDX,ENDY):三次贝塞尔曲线

    S = smooth curveto(S X2,Y2,ENDX,ENDY):三次贝塞尔曲线

    Q = quadratic Belzier curve(Q X Y,ENDX,ENDY):二次贝塞尔曲线

    T = smooth quadratic Belzier curvrto(T,ENDX,ENDY):映射前面路径的重点

    A = elliptical Are(A RX,RY,XROTATION,FLAG1FLAG2,X,Y):弧线

    Z = closepath() 关闭路径
    使用上面的指令时,需要注意的几点

    • 坐标轴以(0,0)位中心,X轴水平向右,Y轴水平向下
    • 所有指令大小写均可,大写绝对定位,参照全局坐标系,小写相对定位,参照父容器坐标系
    • 指令和数据间的空格可以无视
    • 同一指令出现多次可以用一个
    SVG常见指令

    L
    绘制直线的指令是“L”,代表从当前点绘制直线到给定点,“L”之后的参数是一个点坐标,如“L 200 400”绘制直线,同时,还可以使用“H”和“V”指令来绘制水平竖直线,后面的参数是x坐标个y坐标

    M

    M指令类似Android绘图中的path类moveto方法,即代表画笔移动到某一点,但并不发生绘图动作

    A
    A指令是用来绘制一条弧线,且允许弧线不闭合,可以把A指令绘制的弧度想象成椭圆的某一段A指令一下有七个指令

    • RX,RY指所有的椭圆的半轴大小
    • XROTATION 指椭圆的X轴和水平方向顺时针方向的夹角,可以想象成一个水平的椭圆饶中心点顺时针旋转XROTATION 的角度
    • FLAG1 只有两个值,1表示大角度弧度,0为小角度弧度
    • FLAG2 只有两个值,确定从起点到终点的方向1顺时针,0逆时针
      X,Y为终点坐标

    SVG的指令参数非常的复杂,但是再Android中,不需要绘制太多的SVG图像

    关于SVG 可以参考这片文章,挺好的 https://www.jianshu.com/p/0555b8c1d26a

    Android动画特效

    • 卫星菜单

    当点击小红点的时候,弹出菜单,并且带有一个缓冲的效果,这就是Google在MD中强调的动画过渡,要怎么实现这个动画呢,其实还不是就一个开始一个结束动画,看代码

    /**
         * 执行动画
         */
        private void statAnim(){
            ObjectAnimator animator0 = ObjectAnimator.ofFloat(iv0,"alpha",1F,0.5F);
            ObjectAnimator animator1 = ObjectAnimator.ofFloat(iv1,"translationY",200F);
            ObjectAnimator animator2 = ObjectAnimator.ofFloat(iv2,"translationX",200F);
            ObjectAnimator animator3 = ObjectAnimator.ofFloat(iv3,"translationY",-200F);
            ObjectAnimator animator4 = ObjectAnimator.ofFloat(iv4,"translationX",-200F);
            AnimatorSet set = new AnimatorSet();
            set.setInterpolator(new BounceInterpolator());
            set.playTogether(animator0,animator1,animator2,animator3,animator4);
            set.start();
            mFlag = false;
        }
    

    下面就是点击事件

    iv0.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                       if (mFlag){
                           statAnim();
                       }else{
                           closeAnim();
                       }
                }
            });
    
    • 计时器动画

    通过这个示例,我们了解一下ValueAnimator的效果,当用户点击后,数字不断增加,好的,我们开始

    package com.lgl.animations;
    
    import android.animation.ValueAnimator;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.TextView;
    
    /**
     * 绘制SVG
     * Created by LGL on 2016/4/16.
     */
    public class SVGActivity extends AppCompatActivity {
    
        private TextView tv;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_svg);
    
            tv = (TextView) findViewById(R.id.tv);
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    tvTimer(tv);
                }
            });
        }
    
        private void  tvTimer(final  View view){
            ValueAnimator va = ValueAnimator.ofInt(0,100);
            va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    ( (TextView)view).setText("$"+(Integer)animation.getAnimatedValue());
                }
            });
            va.setDuration(3000);
            va.start();
        }
    
    }
    
    
    • 下拉展开动画

    下面我们来实现一个展开的动画,首先,XML是这样的

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_blue_bright"
            android:gravity="center_vertical"
            android:onClick="llClick"
            android:orientation="horizontal">
    
            <ImageView
                android:id="@+id/app_icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@mipmap/ic_launcher" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:gravity="left"
                android:text="Click me"
                android:textSize="30sp" />
    
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/hidden_view"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="@android:color/holo_orange_light"
            android:orientation="horizontal"
            android:visibility="gone">
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@mipmap/ic_launcher" />
    
            <TextView
                android:id="@+id/tv_hidden"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="I Am Hidden"
                android:textSize="20sp" />
    
        </LinearLayout>
    
    </LinearLayout>
    

    为了区分两个不同的线性布局,我们给他设置了不同的颜色背景,

    package com.younger.animations;
    
    import android.animation.ValueAnimator;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.LinearLayout;
    
    /**
     * 下拉展开动画
     * Created by Younger on 2018/03/11.
     */
    public class AnimaActivity extends AppCompatActivity {
    
        private LinearLayout mHiddenView;
        private float mDensity;
        private int mHiddenViewMeasuredHeight;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_anima);
    
            mHiddenView = (LinearLayout) findViewById(R.id.hidden_view);
            //获取像素密度
            mDensity = getResources().getDisplayMetrics().density;
            //获取布局的高度
            mHiddenViewMeasuredHeight = (int) (mDensity*40+0.5);
        }
    
        public  void llClick(View view){
            if (mHiddenView.getVisibility() == View.GONE){
                animOpen(mHiddenView);
            }else{
                animClose(mHiddenView);
            }
        }
    
        private void animOpen(final  View view){
            view.setVisibility(View.VISIBLE);
            ValueAnimator va = createDropAnim(view,0,mHiddenViewMeasuredHeight);
            va.start();
        }
    
    
        private void animClose(final  View view){
            int origHeight = view.getHeight();
            ValueAnimator va = createDropAnim(view,origHeight,0);
            va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    view.setVisibility(View.GONE);
                }
            });
            va.start();
        }
    
    
        private ValueAnimator createDropAnim(final  View view,int start,int end) {
            ValueAnimator va = ValueAnimator.ofInt(start, end);
            va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int value = (int) animation.getAnimatedValue();
                    ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
                    layoutParams.height = value;
                    view.setLayoutParams(layoutParams);
                }
            });
            return  va;
        }
    
    }
    

    好了,就这些

    相关文章

      网友评论

        本文标题:第七章 Android 动画机制与使用技巧

        本文链接:https://www.haomeiwen.com/subject/rfrufftx.html