Android动画之属性动画(上)

前言

       在前面的文章中我们讲述了Android动画之视图动画学习了怎么对一个view实现动画,可以实现动画包括平移,旋转,缩放,渐变和帧动画,这一篇我们来学习一个新的动画实现方式。

属性动画

为什么需要属性动画?

       Android在3.0引入了属性动画,既然之前已经有属性动画了,为什么还要引入属性动画呐?视图动画不好用吗?还是视图动画不能够实现功能?随着系统的迭代更新,和设计的发展,以前做app都是比较简单的,但是现在的app越来越复杂,功能越来越多,动画效果也更加的复杂,更阿基要求实时性和界面的绚丽,因此老的系统很多实现都已经改变,对新的功能也力不从心,所以才引入属性动画来改善动画的效果, 主要有如下原因:

1,视图动画是非交互动画,仅仅只是改变了显示位置
2,视图动画种类有限,复杂的效果力不从心
3,视图动画调用onDraw来重绘界面,耗费资源
4,视图动画只能针对view实现动画,对非view不能实现

       我们分别解释一下上面的四点:

视图动画是非交互动画,仅仅只是改变了显示位置

我们在上一章讲述了视图动画是非交互动画,这个虽然不是一个很大的缺陷,但是在真的需要交互的时候就力不从心了,要实现额外的代码才能实现其效果,

视图动画种类有限,复杂的效果力不从心

其次在现阶段很多app都有非常复杂的效果,仅仅依靠视图动画的组合往往很难实现,就算实现了效果,代码也非常的多,可能难以维护。

视图动画调用onDraw来重绘界面,耗费资源

onDraw来返回重绘界面,这样就导致耗费资源和性能,流畅性是一个app非常重要的体验,虽然单一一个动画不能特别明显的拖慢性能,但是各个地方差值综合就会导致体验很差

视图动画只能针对view实现动画,对非view不能实现

视图动画的种类仅仅只有四种,并且只能针对view实现,如果只是针对一个view的背景颜色,或者其他的属性来实现动画,就不能实现。

属性动画的特点

       我们在上面大书特书了视图动画的不足,那是不是属性动画就完全弥补了视图动画的不足?答案是的:

1,属性动画真正改变的是对象的属性
2,属性动画是可以交互的
3,属性动画针对view与非view都能实现动画
4,如果属性不存在,还可以自定义属性来实现动画

视图动画的使用场景

       虽然属性动画有这样那样的优点,是不是视图动画我们就抛诸脑后,不再使用?是,也不是?如果你新开发一个app,并且不是支持3.0一下的版本,那最好是直接使用属性动画,那什么时候使用视图动画?

1,现有代码已经实现效果
2,视图动画能实现所需要的效果
3,视图动画只需要更少的代码来实现

代码结构

  • Animator
    • ValueAnimator
      • ObjectAnimator
      • TimeAnimator
    • AnimatorSet

       这里我们主要注意一下视图动画与属性动画的异同。代码结构很类似,但是根节点是不一样的,视图动是Animation,属性动画是Animator,视图动画有四个子类,平移,旋转,缩放,渐变,这里完全用ObjectAnimator来代替了。

动画实现方式

xml实现

在res/animator下定义动画文件(ValueAnimator-animator,ObjectAnimator
–objectAnimator, AnimatorSet-set ) 代码中采用AnimatorInflater().
loadAnimator()加载

代码实现

采用ObjectAnimator或者ValueAnimator实现

动画实现

ObjectAnimator

       我们上面讲述了两种动画的实现方式。这里我们先讲述一下代码实现,我们先来看看在视图动画的一个截图,当时实现了平移,旋转,缩放,渐变,那这里我们用属性动画来实现一遍:

这里写图片描述

       在实现这四种动画之前,我们先来讲解一下使用方式,我们先来看看可以使用的api有哪些?

这里写图片描述

       我们从图片可以到,of类型的函数有很多种,有ofArgb,ofFloat,ofInt等,这里有这么多api,那我们使用哪一个?这里我们主要使用ofFloat,因为这个函数是从3.0引入的,这个函数比较通用,其他的函数有的是4.0引入的,5.0引入的。因此这里我们主要讲解ofFloat:

ObjectAnimator.ofFloat(Object target, String propertyName, float... values)

       各个属性的解释如下:

1,target作用的对象
2,propertyName作用的属性,需要有public的setName函数
3,values可变参数,表示动画执行的一组值

       渐变

private void alpha() {
    ObjectAnimator alpha = ObjectAnimator.ofFloat(love, "alpha", 1.0f, 0.0f);
    alpha.setDuration(1000);
    alpha.start();
}

       这里使用的属性是alpha,1.0f表示完全不透明,0.0f表示完全透明。

       平移

private void translate() {
    ObjectAnimator translate = ObjectAnimator.ofFloat(love, "translationX", 200f);
    translate.setDuration(1000);
    translate.start();
}

       这里使用的是translationX属性,只平移了x轴,Y轴不做变化。

       缩放

private void scale() {
    ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f);
    scaleX.setDuration(1000);
    scaleX.start();
    ObjectAnimator scaleY = ObjectAnimator.ofFloat(love, "scaleY", 1.0f, 2.0f);
    scaleY.setDuration(1000);
    scaleY.start();
}

       这里使用了scaleX与scaleY属性,1.0f表示原始大小,2.0f表示放大一倍。默认缩放为中心缩放。
       旋转

private void rotate() {
    ObjectAnimator rotate = ObjectAnimator.ofFloat(love, "rotation", 0f, 360f);
    rotate.setDuration(1000);
    rotate.start();
}

       这里使用的是rotation属性,旋转了360度,默认的旋转中心为视图中心。

       上面我使用了一系列属性,我怎么知道哪些属性可以用哪些属性不能用。这里我们去查找对应view所包含的属性就知道了。

没有对应属性

       上面我们都是使用已知的属性,如果一个view我们想要实现的效果没有对应的属性怎么办?

直接添加set函数
包装原有类,添加set函数
采用ValueAnimator实现

       这里我们解释以下上面的情况,第一种如果你有权限,或者是自定义的view,如果没有对应属性,我们可以手动添加对应的属性,第二种情况,我们可以包装现有类,添加对应的属性,第三种我们可以采用ValueAnimator来实现,这里我们来包装一下原有类,后面我们会讲解ValueAnimator的实现。

       我们就以上述缩放效果来演示,上面我们使用了两个属性scaleX,scaleY来缩放一个view,这里我们用一个属性scale属性来缩放,但是ImageView没有缩放这个属性,那怎么办?

public class WrapperView {

    public ImageView target;

    private float scale;

    public WrapperView(ImageView target) {
        this.target = target;
    }

    public float getScale() {
        return scale;
    }

    public void setScale(float scale) {
        this.scale = scale;
        target.setScaleX(scale);
        target.setScaleY(scale);
    }
}

       我们包装对应的imageview,给他添加对应的scale属性,之后在代码中使用。动画代码如下:

private void addProperty() {

    WrapperView view = new WrapperView(love);
    ObjectAnimator scale = ObjectAnimator.ofFloat(view, "scale", 1.0f, 2.0f);
    scale.setDuration(1000);
    scale.start();
}

       这里我们使用的属性就是上述添加的scale属性。这样我们就实现了缩放的效果。

组合动画

       我们在视图动画时讲过,动画有并行,串行,属性动画也有这些组合。那我们接下来就讲述一下这些效果。

并行动画

       并行动画,就是说多个动画同时执行,比如我们在旋转的同时缩放一个view,这里主要有三种方式实现:

       1:多个ObjectAnimator实现

private void togetherAnim1() {
    ObjectAnimator rotate = ObjectAnimator.ofFloat(love, "rotation", 0f, 360f).setDuration(1000);
    ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f).setDuration(1000);
    ObjectAnimator scaleY = ObjectAnimator.ofFloat(love, "scaleY", 1.0f, 2.0f).setDuration(1000);
//        rotate.start();
//        scaleX.start();
//        scaleY.start();
    AnimatorSet set = new AnimatorSet();
    set.playTogether(rotate, scaleX, scaleY);
    set.setDuration(1000);
    set.start();
}

       这里我们定义了三个ObjectAnimator,之后用AnimatiorSet来进行执行。调用playTogether来同时执行这个三个动画,还有一种方式是,可以分别start三个动画也能实现效果。

这里有人可能会有疑问,连续三个start那不应该是串行的吗?怎么会是并行的呐?其实这里的start,动画并没有立即开始执行,而是等待下一个刷新时间戳过来才执行的。

       2:一个ObjectAnimator实现

private void togetherAnim2() {
    PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
    PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 2.0f);
    PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 2.0f);
    ObjectAnimator together = ObjectAnimator.ofPropertyValuesHolder(love, rotate, scaleX, scaleY);
    together.setDuration(1000);
    together.start();
}

       这里我们使用PropertyValuesHolder来实现,定义三个PropertyValuesHolder,它与ObjectAnimator的区别是没有target属性,之后将PropertyValuesHolder放置到ObjectAnimator里面,之后start ObjectAnimator动画。

       3:采用ViewPropertyAnimator实现

private void togetherAnim3() {
    love.animate().rotation(360f).scaleX(2.0f).scaleY(2.0f).setDuration(1000).start();
}

       该方式系统已经实现了一个帮助方法,animate,之后可以链式的执行各个属性。该方式代码量是最少了的。

串行动画

       前面已经实现了并行,我们调用了playTogether,这里我们只要换成另外一种方法就可以实现串行。

private void sequenceAnim() {
    ObjectAnimator rotate = ObjectAnimator.ofFloat(love, "rotation", 0f, 360f).setDuration(1000);
    ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f).setDuration(1000);
    ObjectAnimator scaleY = ObjectAnimator.ofFloat(love, "scaleY", 1.0f, 2.0f).setDuration(1000);
    AnimatorSet set = new AnimatorSet();
    set.playSequentially(rotate, scaleX, scaleY);
    set.setDuration(1000);
    set.start();
}

       只需要将playTogether换成为playSequentially就可以了。

排列多个动画

       前面我们实现了并行与串行动画,但是如果一个动画既有串行又有并行怎么办?代码如下:

private void compositeAnim() {
    ObjectAnimator translateY = ObjectAnimator.ofFloat(love, "translationY", -200f).setDuration(1000);
    ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f).setDuration(1000);
    ObjectAnimator scaleY = ObjectAnimator.ofFloat(love, "scaleY", 1.0f, 2.0f).setDuration(1000);
    ObjectAnimator rotate = ObjectAnimator.ofFloat(love, "rotation", 0f, -45f).setDuration(1000);
    AnimatorSet set = new AnimatorSet();
    set.play(scaleX).with(scaleY).with(rotate);
    set.play(translateY).after(scaleX);
    set.start();
}

       这里还是采用AnimatorSet来实现,with表示同时执行动画。before方法,before里面的代码后执行,after方法,after里面的动画先执行。

ValueAnimator

       前面我们已经讲解了ObjectAnimator,我们前面的代码结构中还有ValueAnimator,那ValueAnimator是什么呐?

1,ValueAnimator是一个数值发生器
2,ValueAnimator不作用于任何属性
3,需要在onAnimationUpdate获取当前时间点发生的值

       我们解释一下上面的要点,首先ValueAnimator是一个数值发生器,就是他只产生数值,不对任何属性起作用,因此他与ObjectAnimator不同,他不能产生动画,动画需要我们手动来处理。处理的方式就是在onAnimationUpdate中获取产生的值。

       那我们就来用ValueAnimator来实现一个动画,我们实现一个倒计时的动画:

private void countDown() {
    ValueAnimator countDown = ValueAnimator.ofInt(10, 0);
    countDown.setDuration(10000);
    countDown.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            textView.setText("" + animation.getAnimatedValue());
        }
    });
    countDown.start();
}

       这里我们用的ofInt函数,因为倒计时是整数,所以这里用ofInt,我们在对动画设置执行时长,最后在回调中获取产生的值,animation.getAnimatedValue()就是当前产生的值。我们使用这个产生的值,实现倒计时动画效果。

动画监听

       我们已经学习了怎么用ObjectAnimator和ValueAnimator来处理动画,但是如果需要监听动画的开始和结束又怎么办?
我们可以添加监听:


private void alpha() {
    ObjectAnimator alpha = ObjectAnimator.ofFloat(love, "alpha", 1.0f, 0.0f);
    alpha.setDuration(1000);
    alpha.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
            Toast.makeText(ObjectAnimationActivity.this, "start", Toast.LENGTH_LONG).show();
            ;
        }

        @Override
        public void onAnimationEnd(Animator animation) {

        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });
    alpha.start();
}

       我们可以对动画添加Animator.AnimatorListener监听,有四个回调函数,这样我们就可以在动画开始,结束进行处理。

       不过上诉有四个回调,我们往往是不需要处理这么多回调,仅仅只是想处理需要关注的回调,这种情况有解吗?我们可以采用如下的方式进行处理:

private void translate() {
    ObjectAnimator translate = ObjectAnimator.ofFloat(love, "translationX", 200f);
    translate.setDuration(1000);
    translate.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            //TODO
        }
    });
    translate.start();
}

       对动画设置AnimatorListenerAdapter,这里我们只需要关注需要关注的回调。

       还有一种监听方式我们前面已经使用过了。就是ValueAnimator中添加监听,我们可以添加ValueAnimator.AnimatorUpdateListener监听,在onAnimationUpdate回调中进行处理。

插值器

       在视图动画中,我们已经讲解过插值器,这里的插值器与视图动画中的插值器没有什么区别。因此就不在进行讲述了。

总结

       这里我们已经讲述完成了属性动画的实现,还有其他的属性没有实现,在接下来的一篇中我们继续讲述剩下的属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值