欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android动画机制与使用技巧

程序员文章站 2022-06-04 23:36:37
...

导语

Android动画效果一直是人机交互中十分重要的一部分,从早期的Android版本中,由于动画机制和绘图机制的不健全,Android的人机交互备受诟病,Android从4.X开始,特别是5.X,动画越来越完善了,Google也开始重视这一方面了,当然我们也必须重视这一方面,看实例戳我

主要内容

  • Android View动画框架
  • Android属性动画分析
  • Android布局动画
  • Interpolators(插值器)
  • 自定义动画

具体内容

Android View动画框架

Animation动画框架定义了透明度,旋转,缩放个移动等几种动画,而且控制了整个的View,实现原理是每次绘制视图的时候View所在的ViewGroup中drawChild函数获取该View的Animation的Transformation值,然后调用了canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,如果动画没有完成则继续调用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);

我们一起来运行一下看效果:

Android动画机制与使用技巧

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

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) {

                    }
                });

执行之后打印log:

Android动画机制与使用技巧

当然,我们眼见为实:

Android动画机制与使用技巧

通过监听,我们就可以了解动画的动向了。

Android属性动画分析

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

ObjectAnimator

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

Android动画机制与使用技巧

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

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

ObjectAnimator ob = ObjectAnimator.ofFloat(
            view, 
            "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、set方法。或者通过ValueAnimator来实现,ValueAnimator在后面的内容中会讲到,这里先来看看如何使用包装类的方法给一个属性增加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方法,使用时只需要操作包装类就可以间接调用到get、set方法了。

WrapperView vi = new WrapperView(mButton);
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(alpha,pvh1,pvh2,pvh3).setDuration(2000).start();
ValueAnimator

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

public final class ObjectAnimator extends ValueAnimator

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

ValueAnimator va = ValueAnimator.ofFloat(0,100);
                va.setTarget(view);
                va.setDuration(2000).start();
                va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float values = (float) animation.getAnimatedValue();
                        Log.i("数值",values+"");
                    }
                });

我们运行一下:

Android动画机制与使用技巧

动画事件的监听

一个完整的动画是具有: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来让你自己选择监听事件。

va.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中,我们也去玩玩:

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

</objectAnimator>

在代码中引用:

    /**
     * 引用xml动画
     * @param v
     */
    private void scaleX(View v){
        Animator anim = AnimatorInflater
                .loadAnimator(this,R.animator.animator);
        anim.setTarget(v);
        anim.start();
    }
View的animate方法

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

 animate.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"

不过这都是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);

通过上面。就可以给布局正加一个视图动画,让子View出现的时候有一个缩放动画,在apiDemo中这个例子还是很经典的。

LayoutAnimationController的第一个参数是需要作用的动画,而第二个参数,刚是每个子View显示的delay的时间。当delay时间不为0时,可以设置子View显示的顺序:
- LayoutAnimationController.ORDER_NORMAL——顺序。
- LayoutAnimationController.ORDER_RANDOM——随机。
- LayoutAnimationController.ORDER_REVEESE——反序。

Interpolators(插值器)

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

Android动画机制与使用技巧

自定义动画

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

@Overrideprotected 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动画框架,会使用Android简单视图动画。
  • 学会使用Android属性动画开发出更加丰富的动画效果。
  • 简单使用布局动画。
  • 使用Interpolators(插值器)自定义动画。

进入我的CSDN戳这里(我的博客导航)