android开发艺术探索(七)

Android动画深入分析

Android动画可以分为三种:View动画、帧动画、属性动画。
View动画
View动画的作用对象是View,它支持四种动画效果,分别是平移动画(TranslateAnimation)、缩放动画(ScaleAnimation)、旋转动画(RotateAnimation)、和透明度动画(AlphaAcnimation)。除此之外,还提供了动画集合AnimationSet动画集合,混合使用多种动画。

使用动画方式:
1、代码中加载xml动画文件。我们再res/anim文件夹下新建view_alpha.xml文件。

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="0"
    android:toAlpha="1"
    >
</alpha>

在代码中加载此动画:

AlphaAnimation alpha= (AlphaAnimation) AnimationUtils.loadAnimation(MainActivity.this,R.anim.view_alpha);
 //设置动画持续时间
                alpha.setDuration(1000);
                //View动画开始
                ivPhoto.startAnimation(alpha);

2、在代码中构建动画

  /**透明度动画,第一个参数为起始透明度
                 * 第二个参数为结束时透明度
                 * */
                AlphaAnimation alpha=new AlphaAnimation(0,1);
                //设置动画持续时间
                alpha.setDuration(1000);
                //View动画开始
                ivPhoto.startAnimation(alpha);

我们先简单看一下动画的固定语法:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true"
    android:duration="1000"
    android:fillAfter="true"
    >
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"></alpha>

    <scale
        android:fromXScale="0.5"
        android:fromYScale="0.5"
        android:pivotX="0"
        android:pivotY="0"
        android:toXScale="1.5"
        android:toYScale="1.5"></scale>
    <translate
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="100"
        android:toYDelta="0"></translate>
    <rotate
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="225"></rotate>
</set>

set标签,对应AnimationSet类,表示动画合集。

interpolator:表示动画集合所采用的插值器,插值器影响动画的速度。
shareInterpolator:表示动画集合是否共享同一个插值器。
duration:表示动画的持续时间。
fillAfter:动画结束后,View是否停留在结束位置,true表示View停留在结束位置,false则不停留。

translate标签:对应TranslateAnimation类,它可以使一个view在水平和竖直方向完成平移的动画效果。

fromXDelta:平移初始位置的x值
fromYDelta:平移初始位置的y值
toXDelta:平移结束位置的x值。
toYDelta:平移结束位置的y值。

scale标签:对应ScaleAnimation类,他可以使View具有放大或者缩小的动画效果。

fromXScale:水平方向缩放的起始值。
toXScale:水平方向缩放的结束值。
fromYScale:竖直方向缩放的起始值。
toYScale:竖直方向缩放的结束值。
pivotX:缩放的轴点x坐标,它会影响缩放的效果。
pivotY:缩放的轴点y坐标,会影响缩放的效果。
默认情况下,缩放的轴点是View的中心点

rotate:旋转动画,对应RotateAnimation,它可以使View具有旋转的动画效果。

fromDegrees:旋转开始的角度。
toDegrees:旋转结束的角度。
pivotX:旋转的轴点的x坐标。
pivotY:旋转的轴点的y坐标。

alpha:透明度动画,对应AlphaAnimation,可以改变View的透明度。

fromAlpha:表示透明度的起始值。
toAlpha:表示透明度的结束值。

代码生成:
1、透明度动画

  /**透明度动画,第一个参数为起始透明度
                 * 第二个参数为结束时透明度
                 * */
                AlphaAnimation alpha=new AlphaAnimation(0,1);
                alpha.setDuration(1000);
                ivPhoto.startAnimation(alpha);

2、旋转动画

 /**参数一:代码起始角度
                * 参数二:代码结束角度
                * 参数三和参数四为大妈旋转中心点坐标的x、y值
                * */
                RotateAnimation ra=new  RotateAnimation(0,360,100,100);
                ra.setDuration(1000);
                ivPhoto.startAnimation(ra);

同时也可以设置参考系为自身中心点:

RotateAnimation ta=new RotateAnimation(0,360,
                        Animation.RELATIVE_TO_SELF,0.5f,
                        Animation.RELATIVE_TO_SELF,0.5f);
                ta.setDuration(1000);
                ivPhoto.startAnimation(ta);

3、平移动画

  TranslateAnimation ta=new TranslateAnimation(0,200,0,300);  
                ta.setDuration(1000);
                ivPhoto.startAnimation(ta);

4、缩放动画

 ScaleAnimation sc=new ScaleAnimation(0,2,0,2);
                sc.setDuration(1000);
                ivPhoto.startAnimation(sc);

同时也可以设置缩放的中心点:

 ScaleAnimation sc=new ScaleAnimation(0,2,0,2
                , Animation.RELATIVE_TO_SELF,0.5f,
                        Animation.RELATIVE_TO_SELF,0.5f);
                sc.setDuration(1000);
                ivPhoto.startAnimation(sc);

5、动画合集:
通过AnimationSet,可以将动画以组合的形式展现出来:

AnimationSet set=new AnimationSet(true);
                set.setDuration(1000);

                AlphaAnimation aa=new AlphaAnimation(0,1);
                aa.setDuration(1000);
                /**将此动画加入到集合中*/
                set.addAnimation(aa);

                TranslateAnimation ta1=new TranslateAnimation(0,100,0,200);
                ta1.setDuration(1000);
                /**将此动画加入到集合中*/
                set.addAnimation(ta1);

                ivPhoto.startAnimation(set);

对于动画时间,android也提供了对应的监听回调,要添加相应的监听方法:

 sc.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                        /**动画开始*/
                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        /**动画结束*/
                    }
                    @Override
                    public void onAnimationRepeat(Animation animation) {
                        /**动画结束*/
                    }
                });         

自定义View动画
除了系统提供的动画之外,我们还可以自定义动画。

1:继承Animation抽象类,重写initialize和applyTransformation方法
2:在initialize方法中进行初始化操作
3:在applyTransformation方法总进行相应的矩阵变换,一般采用Carmera简化矩阵变换。

帧动画
帧动画是顺序播放一组预先定义好的图片,类似于电影播放。系统提供了一个类AnimationDrawable来使用帧动画。
在res/drawable下新建动画view_animation_drawable.xml文件,如下:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@mipmap/ic_launcher" android:duration="500"></item>
    <item android:drawable="@mipmap/ic_launcher" android:duration="500"></item>
    <item android:drawable="@mipmap/ic_launcher" android:duration="500"></item>
</animation-list>

然后将上述的Drawable作为View的背景并通过Drawable来播放动画即可:

 ivPhoto.setBackgroundResource(R.drawable.view_animation_drawable);
        AnimationDrawable ad= (AnimationDrawable) ivPhoto.getBackground();
        ad.start();

注意:帧动画比较容易引起OOM,使用时尽量避免使用尺寸较大的图片。

View动画的特殊使用场景
View动画可以在一些特殊的场景下使用:

Viewgroup中可以控制子元素的出场效果
在Activity中可以实现不同的Activity之间的切换效果。

LayoutAnimation

LayoutAnimation作用于Viewgroup,为ViewGroup指定一个动画,使它的子元素出场时具有一定的动画效果。LayoutAnimation也是一个View的动画。

使用步骤:
Step1:定义LayoutAnimation
在res/anim下新建view_layout_animation.xml

<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.5"
    android:animationOrder="normal"
    android:animation="@anim/anim_item"
    >
</layoutAnimation>

属性含义:

delay:表示子元素开始动画的时间延迟。
android:animationOrder:表示子元素动画的顺序,有三种选项:noraml、reverse和random其中normal表示顺序显示;reverse表示逆向显示;random则是随机播放入场动画。
android:animation:为子元素指定具体的入场动画

Step2:为子元素指定具体的入场动画
在res/anim下新建anim_item.xml文件

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:shareInterpolator="true">
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"></alpha>

    <translate
        android:fromXDelta="500"
        android:toXDelta="0"
        ></translate>
</set>

Step3:为ViewGroup指定android:animation=”@anim/anim_item”属性

 <ListView
        android:id="@+id/lv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layoutAnimation="@anim/view_layout_animation"
        ></ListView>

除了在XML中指定LayoutAnimation之外,还可以通过LayoutAnimationController来实现。

Animation animation= AnimationUtils.loadAnimation(this,R.anim.anim_item);
        LayoutAnimationController controller=new LayoutAnimationController(animation);
        controller.setDelay(0.5f);
        controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
        lvList.setLayoutAnimation(controller);

Activity的切换效果
Activity有默认的切换效果,但是这个效果我们是可以自定义的,主要用到overridePendingTransition(int enterAnim, int exitAnim)这个方法。注意:次方法必须在startActivity(intent)或者finish()之后调用才能生效。

enterAnim:activity被打开时,所需的动画资源id;
exitAnim:activity被暂停时,所需的动画资源id;
例如:

  Intent intent=new Intent(this,ActivityA.class);
        startActivity(intent);
        overridePendingTransition(R.anim.slide_right_in,R.anim.slide_right_out);

右侧进入动画:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="100.0%p"
        android:toXDelta="0.0" />
</set>

右侧移出动画:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="300"
        android:fromXDelta="0.0"
        android:toXDelta="100.0%p" />
</set>

Android属性动画分析
对于View动画来说,动画改变的只是显示,并不能影响时间,因此在3.0之后,android退出了属性动画。

ObjectAnimator
ObjectAnimator是属性动画框架中最重要的实行类。我们可以通过ObjectAnimator的静态工厂类获取ObjectAnimator对象。

我们来实现一个简单的平移动画:

/**第一个参数为要操作的View,第二个参数为要操作的属性,而最后一个参数是一个可变数组参数*/
  ObjectAnimator animator=ObjectAnimator.ofFloat(ivPhoto,"translationX",300);
        animator.setDuration(300);
        animator.start();

PropertyValuesHolder
类似视图动画中的AnimationSet,在属性动画中,如果针对于同一个对象的多个属性,需要同时作用多种动画,可以使用PropertyValuesHolder来实现。
比如平移的时候进行动X、Y轴的动画缩放,如下:

PropertyValuesHolder pv1=PropertyValuesHolder.ofFloat("translationX",300f);
        PropertyValuesHolder pv2=PropertyValuesHolder.ofFloat("scaleX",1f,0,1f);
        PropertyValuesHolder pv3=PropertyValuesHolder.ofFloat("scaleY",1f,0,1f);
        ObjectAnimator.ofPropertyValuesHolder(ivPhoto,pv1,pv2,pv3)
                .setDuration(1000).start();

ValueAnimator
ValueAnimator在属性动画中占有很重要的地位,ObjectAnimation也是继承自ValueAnimator;

ValueAnimator本身并不提供任何动画效果,它更像是一个数值发生器,用来产生具有一定规律的数字,从而让调用者来实现动画的实现过程。
使用方法:通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值的变换,从而完成动画的变换。

   ValueAnimator animator1=ValueAnimator.ofFloat(0,100);
        animator1.setTarget(ivPhoto);
        animator1.setDuration(1000).start();
        animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Float value= (Float) valueAnimator.getAnimatedValue();
            }
        });

动画事件的监听
一个完整个的动画具有Start、Repeat、End、Cancel四个过程,通过Android提供了接口,可以很方便的监听事件:

  ObjectAnimator obj1=ObjectAnimator.ofFloat(ivPhoto,"alpha",0.5f);
        obj1.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                /**动画开始*/
            }
            @Override
            public void onAnimationEnd(Animator animator) {
                /**动画结束*/
            }
            @Override
            public void onAnimationCancel(Animator animator) {
                /**动画取消*/
            }
            @Override
            public void onAnimationRepeat(Animator animator) {
                /**动画重复*/
            }
        });

我们可以使用AnimatorListenerAdapter进行事件筛选。

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

AnimatorSet
动画合集,和PropertyValuesHolder类似,但是AnimationSet可以提供更精确的顺序控制。

 ObjectAnimator as1=ObjectAnimator.ofFloat(ivPhoto,"translationX",300f);
        ObjectAnimator as2=ObjectAnimator.ofFloat(ivPhoto,"scaleX",1f,0,1f);
        ObjectAnimator as3=ObjectAnimator.ofFloat(ivPhoto,"scaleY",1f,0,1f);
        AnimatorSet set=new AnimatorSet();
        set.setDuration(1000);
        set.playTogether(as1,as2,as3);
        set.start();

在XML中使用属性动画
在res/animator文件夹下新建scalex.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 at= AnimatorInflater.loadAnimator(this,R.animator.scalex);
        at.setTarget(ivPhoto);
        at.start();

View的animate方法
3.0之后,googel给View增加了animate方法来直接驱动属性动画,代码如下:

  ivPhoto.animate()
                .alpha(0).y(300)
                .setDuration(300)
                .withStartAction(new Runnable() {
                    @Override
                    public void run() {
                    }
                })
                .withEndAction(new Runnable() {
                    @Override
                    public void run() {

                    }
                }).start();

理解插值器和估值器
TimeInterpolator中文翻译为事件插值器,作用是根据时间流逝的百分比来计算出当前属性值改变的百分比。
备注:

自定义插值器需要实现Interpolator或者TimeInterpolator,自定义估值算法需要实现TypeEvaluator,另外就是如果要对其他类型(非int、float、Color)做动画,那么必须奥自定义类型估值算法。

属性动画的监听器

属性动画提供了监听器用于监听动画的播放过程,主要有两个接口:
AnimatorListener和AnimatorUpdateListener;

前面已经介绍过了,这里就不说了。

对任意属性做动画
注意:在使用ObjectAnimator 的时候,需要操作的属性必须具有get和set方法,不然ObjectAnimator 就无法起作用。

如果一个属性没有get和set方法,如何来使用?

google在应用曾提供了两种方案,
1、通过自定义一个属性类,或者包装类,来简介的给这个属性增加get和set方法,
2、通过ValueAnimation来实现。

(1)使用包装类

public 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();
    }
}

通过以上代码,就给一个属性包装了一层,并且给他提供了get、set方法。使用时,只需要操纵包专类就可以间接地调用到get、set方法了。

    WrapperView view=new WrapperView(ivPhoto);
        ObjectAnimator.ofFloat(view,"width",500).setDuration(5000).start();

(2)采用ValueAnimation,监听动画过程,自己实现属性的改变
ValueAnimation可以对一个值做动画,然后我们可以监听其动画过程,在动画过程中修改我们对象的属性值。

属性动画的工作原理

属性动画要求作用的对象提供属性的set方法,属性动画根据你传递的该属性的初始值和最终值,以动画的效果多次去调用set方法。每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。
如果动画的时候没有传递初始值,那么还要提供set方法,因为系统要去获取属性的初始值。

使用动画的注意事项

(1)OOM问题

这个问题主要出现在帧动画中,当图片数量较多且图片较大时就容易出现OOM,尽量避免使用帧动画。

(2)内存泄漏

在属性动画中有一类无线循环的动画,这类动画需要在activity退出时及时停止,否则将导致actiivty无法释放而造成内存泄漏。

(3)兼容性问题

动画在3.0以下的系统上有兼容性问题,在某些特殊场景可能无法正常工作,因此要做好适配工作。

(4)View动画的问题

View动画是对View的影响做动画,并不是真正的改变View的状态,因此会出现东湖完成后无法隐藏的现象,即setVisibility(View.GONE)失效,这个时候要调用View.clearAnimation()清除View动画即可解决此问题。

(5)不要使用px

在进行动画的过程中,要尽量使用dp,使用平px会在不同的设备上有不同的效果。

(6)动画元素的交互

将View平移后,在android3.0以前的系统上,不管是View动画还是属性动画,新位置均无法触发单击事件,老位置仍然可以触发单击事件。

(7)硬件加速
使用动画过程中,建议开启硬件加速,会提高动画的流畅性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值