Android动画之属性动画

Android动画之属性动画

        在日常开发中,我们可能会需要实现各种炫酷的效果,比如旋转、放大、缩小等,在Android 3.0以前,我们可以通过逐帧动画和补间动画来实现,而在Android 3.0之后,Andorid 新增了属性动画,那么属性动画和其他两种动画又有什么区别呢?

一、三种动画对比

  • 逐帧动画:

            通常我们将许多图片连贯起来播放,这样看起来就是动画的效果,这样的动画就叫做逐帧动画。
            优点:简单
            缺点:动画的效果取决于我们放入的图片,对于开发而言,可操作性较小,更多的则是依于放入的图片(如果动画复杂,可能放入十几二十个图)。
  • 补间动画

            补间动画通常是我们定义好了动画开始和结束的状态,然后由系统来完成中间的计算工作,然后呈现出动画的效果。
            优点:和逐帧动画相比,我们可以实现的动画效果更加复杂,同时也不依赖与图片的导入。
            缺点:这里的动画只是在绘制上发生了改变,也就是视觉上的改变,但是view的属性并没有改变。比如说讲一个按钮位移后,点击位移后的位置无法触发点击事件,而点击按钮动画前的位置则可以触发点击事件,说明view的属性并没有改变。
                        其次,补间动画的效果只能局限于位移、旋转、放缩、透明度四种动画,其他的动画需要我们自己去实现。
  • 属性动画

          属性动画则是在补间动画的基础上,同样我们也会定义开始和结束状态,然后由系统 去计算。
            优点:不仅仅改变了界面显示也改变了view或者其他对象的属性;
                        不仅仅作用与view,还可以作用与其他对象;
                        动画类型不局限与四种简单的动画操作。

            通过上面的对比,我们对属性动画也有了一个基本的认识,一定要记住,属性动画改变的是对象的属性,这一点属性动画这个名称上也能看出来。


二、属性动画之初识

        在了解属性动画具体的使用前,首先我们来看两个类ValueAnimator和ObjectAnimator,通过这两个类我们从原理上来分析属性动画。

    1、Animator

            Animator是属性动画的抽象基类,我们常用到的AnimatorSet、ValueAnimator、ObjectAnimator都是它的子类,在这个抽象类中定义了一些我们常用的方法,这里我们列举一些常见的方法:


方法名
说明
添加动画监听
setDuration (long duration)
设置动画执行时间,单位ms
设置插值器
start ()
开始动画
end ()
结束动画

    2、ValueAnimator

           继承关系:
                ValueAnimator->Animator->Object     
          也就是说ValueAnimator是Animator的子类。而前面我们介绍的Animator中主要包含了关于动画的开始i、结束、设置执行时间、添加监听器、设置插值器等,而我们设置开始和结束值然后计算并            实现动画则是在ValueAnimator中来完成的。
    例如:我们实现一个从0到1的动画变化,很简单,如下:

 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
 anim.setDuration(300);  
 anim.start();
这里我们就实现了一个从0 到 1 的变化,但是很可惜我们运行后看不到任何变化,为什么呢?
原因是我们这里确实是一个动画,也确实是一个从0到1的动画,但是它只是对值做了动画,并没有对对象做动画,也没有和任何对象绑定,更没有与对象的某个属性相绑定,因此我们看不到任何效果,不那么我们如何实现对象的属性动画呢?
这就需要提到我们今天的重点:ObjectAnimator

    

3、ObjectAnimator

            ObjectAnimator是我们常用的属性动画类,它继承了ValueAnimator,实现了和对象属性的绑定。
         (1)ObjectAnimator与对象属性的关系
              前面我们提到,属性动画实际上是改变了对象的属性,那么ObjectAnimtor又是如何改变对象的属性呢?
              这里我们以改变TextView的透明度为例子:

ObjectAnimator animator = ObjectAnimtor.ofFloat(textView,"aplpha",1f,0f,1f);
animator.setDuration(1000);
animator.start();
这里我们需要注意 ObjectAnimtor.ofFloat(textView,"aplpha",1f,0f,1f);我们在ObjectAnimator的ofFloat()方法的第二个参数中输入"aplpha",而在之后的参数中则输入透明度的变化,这里是从1->0->1,经过三个变化。那么是否可以认为是因为我们改变了TextView中有alpha这个属性,所以改变了其透明度?

答案是错误的,因为TextView中并没有alpha属性,那么我们这里是如何改变TextView的属性呢?
事实上,ObjectAnimator是通过执行动画的对象的setX()和getX()来实现对该对象的属性的改变(这不正是我们当时刚学JAVA时提到的通过get和set方法来改变对象属性j),而这里的X则是我们在ofFloat中输入的alpha,也就是说我们上面的例子中是通过textView的setAlpha()来改变其透明度。
那么它又是如何通过这个setAlpha()实现从透明到不透明再到透明的动态效果呢?
这个就需要谈到属性动画的插值器,这个我们下一节会重点讲插值器。

        (2)常见的ObjectAnimator动画
        我们通常通过使用public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
            <1>第一个参数  Object target                                     Object对象,可以看到这里只要是Object对象就可以,不局限于view
            <2>第二个参数String propertyName                         属性名称 ,比如透明度"alpha"’、旋转"rotation"
            <3>第三个参数float... values                                        不定长变量,表示对应属性的变化值,比如说位移动画,传入10,表示向右平移10

    这里我们需要区分一个概念,就是第三个参数float... values 是相对值还是绝对值,怎么理解这句话呢?
    就拿x轴位移来说,我们传入一个x1,x2,那么这里x1,x2是表示屏幕上的坐标(x1,y1),(x2,y2)呢,还是说移动的距离是x1,x2呢,这是完全不同的两个概念的。其实对于属性动画而言,我们和实际的坐标点并不挂钩,也就是我们是相对于对象自身来实现对象,也就是说我们的值是相对值,相对于我们view,比如说这里的x1,其实表示的是view向右平移10,而不是说移动到坐标点(10,y),那么我们传入的x1具体对应到屏幕上是哪个点则不需要我们去管,view自己会根据我们传入的变化值去计算。

  • 透明度
ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);
ObjectAnimator.ofFloat(textView,"alpha",0f);
ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f,1f,0f,1f);//这里表示textview在执行动画前是不透明,然后第一个状态1f,仍然是不透明,然后第二个状态0f,则由不透明变为透明,之后第三个状态1f,则由透明变为不透明

  • 位移
ObjectAnimator.ofFloat(textview,"translationX",10);//这里没有设置初始状态,因为ofFloat方法中只需要设置结束状态就可以,这里的含义是view沿x轴方向向右平移10
ObjectAnimator.ofFloat(textview,"translationX",-10);//这里的含义是view沿x轴方向向左平移10
ObjectAnimator.ofFloat(textview,"translationX",0,10);//这里的含义是view沿x轴方向向右平移10,相当于第一个的情形
ObjectAnimator.ofFloat(textview,"translationX",10,20);//这里的含义是view沿x轴方向向右平移10距离,继续享向右平移20的距离
ObjectAnimator.ofFloat(textview,"translationX",0,-10,20,30);//这里的含义是view沿x轴方向向左平移10距离,然后向右平移20的距离,再向右平移30的距离

  • 缩放
ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);  

  • 旋转
ObjectAnimator.ofFloat(textview, "rotation" 10f, 360f);

            

4、组合动画

        在很多时候我们会将旋转、位移、放缩等结合在一起使用,此时就需要用到组合动画,顾名思义就是将动画效果组合在一起。
        实现组合动画,我们就需要使用AnimatorSet这个类,AnimatorSet这个类也是继承了Animator抽象类。而要实现组合动画,那么就需要以下四个方法:
  • play(Animator anim),表示执行anim动画
  • after(Animator anim1),表示将anim1动画插入到现有动画之后执行
  • after(long delay ),表示将现有动画延时delay毫秒
  • before(Animator anim),​表示将anim动画插入到现有动画之前执行
  • with(Animator anim),表示将anim动画和现有动画同时执行
范例:
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();

可以看到fadeInOut和rotate同时执行,之后执行moveIn。
以上四个方法中均需要传入Animtor对象,比如说ValueAnimator或者是ObjectAniamtor对象。

三、Animator监听器

        前面我们已经实现了基本的属性动画,那么接下来考虑这样一个场景,设计一个滑块,可以在屏幕内随机取点滑动,每滑到一个点,停留一秒,然后再滑动到下一个点。
这样我们就需要考虑以下两个问题:
  • 如何实现滑动?
  • 在代码中我们如何知道滑块已经滑动到了某一个点还是说它还在滑动过程中?​
       首先我们解决第一个问题,滑动问题我们可以通过平移动画来解决。如下:
        假设初始状态下滑块的点的位置为(x0,y0),我们需要移动到第二个点(x1,y1)。

animatorX = ObjectAnimator.ofFloat(mIndicator, "translationX",  x1-x0);
animatorY = ObjectAnimator.ofFloat(mIndicator, "translationY", y1-y0);
        这样我们的平移就解决了。那么如何在滑动完成后延时1S,然后移动到下一个点?简化这个问题,我们如何知道一次动画执行结束了?
这里我们就需要用到动画的监听器AnimatorListener

public static interface AnimatorListener {
    /**
     * <p>Notifies the start of the animation.</p>
     *
     * @param animation The started animation.
     */
    void onAnimationStart(Animator animation);

    /**
     * <p>Notifies the end of the animation. This callback is not invoked
     * for animations with repeat count set to INFINITE.</p>
     *
     * @param animation The animation which reached its end.
     */
    void onAnimationEnd(Animator animation);

    /**
     * <p>Notifies the cancellation of the animation. This callback is not invoked
     * for animations with repeat count set to INFINITE.</p>
     *
     * @param animation The animation which was canceled.
     */
    void onAnimationCancel(Animator animation);

    /**
     * <p>Notifies the repetition of the animation.</p>
     *
     * @param animation The animation which was repeated.
     */
    void onAnimationRepeat(Animator animation);
}

这里主要是四个方法:
  • onAnimationStart:动画开始时调用
  • onAnimationEnd:动画结束后调用​
  • onAni​mationCancel:动画取消时调用
  • onAnimationRepeat:动画重复时调用​

结合到我们上面提到的例子,如下:

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

    }
});

        确实,通过这样我们实现了在动画接收后执行延时和重新平移的操作,但是我们只需要onAnimationEnd(),而这里确调用了四个方法,显得很繁琐,因此我们可以考虑调用AnimatorListenerAdapter,如下:

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

这样我们只需要调 onAnimationEnd()这一个方法就可以了。

四、XML实现动画

        在Andorid的目录结构下,我在res中创建一个animator的目录(注意区别anim目录,它是用来呈现补间动画和逐帧动画),然后再声明属性动画。

1、标签

<animaot>对应ValueAnimator,一般很少使用
<objectAnimator>对应ObjectAnimator
<set>对应AnimatorSet

2、范例

(1)动画文件 btn_anim.xml

    android:ordering="sequentially" >

    <objectAnimator
        android:duration="2000"
        android:propertyName="translationX"
        android:valueFrom="-500"
        android:valueTo="0"
        android:valueType="floatType" >
    </objectAnimator>

    <set android:ordering="together" >
        <objectAnimator
            android:duration="3000"
            android:propertyName="rotation"
            android:valueFrom="0"
            android:valueTo="360"
            android:valueType="floatType" >
        </objectAnimator>

        <set android:ordering="sequentially" >
            <objectAnimator
                android:duration="1500"
                android:propertyName="alpha"
                android:valueFrom="1"
                android:valueTo="0"
                android:valueType="floatType" >
            </objectAnimator>
            <objectAnimator
                android:duration="1500"
                android:propertyName="alpha"
                android:valueFrom="0"
                android:valueTo="1"
                android:valueType="floatType" >
            </objectAnimator>
        </set>
    </set>
</set>

(2)代码

Animator anim = AnimatorInflater.laodAnimator(this,R.animator.btn_anim);
animator.setTargt(view);
animator.start();










   


        



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值