美文网首页
属性动画

属性动画

作者: A_Coder | 来源:发表于2016-11-07 20:42 被阅读0次

属性动画实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。属性动画机制已经不只是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。所以我们仍然可以将一个View进行移动或者缩放,但同时也可以对自定义View中的Point对象进行动画操作了。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束值,剩下的工作就可以全部交给系统去完成了。


  • ValueAnimator
    ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,确实是一个非常重要的类。
    示例:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
anim.setDuration(300);  
anim.start()

setStartDelay()方法来设置动画延迟播放的时间
setRepeatCount()设置动画循环播放的次数
setRepeatMode()方法设置循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思。


  • ObjectAnimator
    ObjectAnimator是经常接触到的类,它是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的。提供了ofInt、ofFloat、ofObject。
    示例:
float curTranslationX = textview.getTranslationX(); 
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX); 
animator.setDuration(5000); 
animator.start();

调用了TextView的getTranslationX()方法来获取到当前TextView的translationX的位置,然后ofFloat()方法的第二个参数传入"translationX",紧接着后面三个参数用于告诉系统TextView应该怎么移动。ofFloat()方法的第二个参数可以是

  • alpha:表示View对象的透明度
  • rotation、rotationX、rotationY:控制View对象围绕支点进行2D和3D旋转
  • translationX、translationY:作为一种增量来控制着View对象从它布局容器的左上角坐标开始的位置。
  • x、y:描述了View对象在它的容器中的最终位置,是最初的左上角坐标和translationX和translationY值得累加
  • scaleY、scaleX:控制View对象围绕它的支点进行2D缩放
    ObjectAnimator内部的工作机制并不是直接对传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法,这两个方法是由View对象提供的,因此alpha属性所对应的get和set方法应该就是:
public void setAlpha(float value);  
public float getAlpha();

  • 组合动画
    实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
    • after(Animator anim) 将现有动画插入到传入的动画之后执行
    • after(long delay) 将现有动画延迟指定毫秒后执行
    • before(Animator anim) 将现有动画插入到传入的动画之前执行
    • with(Animator anim) 将现有动画和传入的动画同时执行

示例:
TextView先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作。

ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textView, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textviwe, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();

  • Animator监听器
    Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了。
    ValueAnimator、ObjectAnimator、AnimatorSet都是继承自Animator的,都可以使用addListener()这个方法。
    添加一个监听器的代码如下:
anim.addListener(new AnimatorListener(){
    @Override
    public void onAnimationStart(Animator animator){ //在动画开始的时候调用
   
    }

     @Override
    public void onAnimationRepeat(Animator animator){ //动画重复执行的时候调用
    }

     @Override
    public void onAnimationEnd(Animator animator){ //在动画结束的时候调用
        //删除动画
        Log.e(TAG, "onAnimationEnd");  
        ViewGroup parent = (ViewGroup) mBlueBall.getParent();  
        if (parent != null)  
            parent.removeView(mBlueBall);                 
    }

       @Override
    public void onAnimationCancel(Animator animator){ //在动画被取消的时候调用
    }
});

有时并不用将这四个接口都实现,Android提供了一个适配器类,叫作AnimatorListenerAdapter,使用这个类就可以解决掉实现接口繁琐的问题了,如下所示:

anim.addListener(new AnimatorListenerAdapter(){
});

向addListener()方法中传入这个适配器对象,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。因此,如果想监听动画结束这个事件,就只需要单独重写这一个方法就可以了,如下所示:

anim.addListener(new AnimatorListenerAdapter(){
    @Override
    public void onAnimationEnd(Animator animator){ //在动画结束的时候调用
    }
});

  • 使用XML编写动画
    通过XML来编写动画在重用方面将会变得非常轻松,比如某个通用的动画编写到XML里面,可以在各个界面当中轻松去重用它。
    如果想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在这个文件夹当中。然后在XML文件中我们一共可以使用如下三种标签:
<animator>  对应代码中的ValueAnimator
<objectAnimator>  对应代码中的ObjectAnimator
<set>  对应代码中的AnimatorSet

示例:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  **
    android:valueTo="0"  **
    android:valueType="floatType"  **
    android:propertyName="alpha"/>**

使用XML来完成复杂的组合动画操作:

<set xmlns:android="http://schemas.android.com/apk/res/android"
        android:ordering="sequentially">
        <objectAnimator
            andorid:duration="2000"
            android:propertyName="translationX"
            android:valueFrom="-500"
            andorid:valueTo="0"
            android:valueType="floatType"
        />
        <set android:ordering="together"
            <objectAnimator
                andorid:duration="2000"
                android:propertyName="rotation"
                android:valueFrom="0"
                andorid:valueTo="360"
                android:valueType="floatType"
            />
        <set android:ordering="sequentially">
            <objectAnimator
                andorid:duration="2000"
                android:propertyName="alpha"
                android:valueFrom="1"
                andorid:valueTo="0"
                android:valueType="floatType"
            />
            <objectAnimator
                andorid:duration="2000"
                android:propertyName="alpha"
                android:valueFrom="0"
                andorid:valueTo="1"
                android:valueType="floatType"
            />
        </set>
    </set>
</set>

使用set标签,orderring属性设置为together,sequentially表示一个接一个执行
在代码中把文件加载进来并将动画启动:

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file); 
animator.setTarget(view);  
animator.start();

调用AnimatorInflater的loadAnimator来将XML动画文件加载进来,然后再调用setTarget()方法将这个动画设置到某一个对象上面,最后再调用start()方法启动动画。

  • View的animate()方法
    animate()方法是属性动画的一种简写方式
view.animate()
            .alpha(0)
            .y(300)
            .setDuration(300)
            .withStartAction(new Runnable() {
                @Override
                 public void run(){
                 } 
            })
            .withEndAction(new Runnable() {
                  @Override
                  public void run(){
                      runOnUIThread(new Runnable(){
                            @Override
                            public void run(){
                            }
                      });      
                  }
            }).start();

  • ValueAnimator的高级用法
    如果有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。
    TypeEvaluator的作用就是告诉动画系统如何从初始值过度到结束值。ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,就是因为系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:
public class FloatEvaluator implements TypeEvaluator{
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction*(((Number) endValue).floatValue() - startFloat);
    }
}

loatEvaluator实现了TypeEvaluator接口,然后重写evaluate()方法。evaluate()方法当中传入了三个参数,第一个参数fraction用于表示动画的完成度的,根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。

  • 对象的动画操作
    因为系统完全无法知道如何从初始对象过度到结束对象,需要实现一个自己的TypeEvaluator来告知系统如何进行过度。
public class Point {
    private float x;
    private float y;

    public Point(float x, float y){
        this.x = x;
        this.y = y;
    }
    public float getX(){
        return x;
    }
    public float getY(){
        reutrn y;
    }
}

Point类只有x和y两个变量用于记录坐标的位置,接下来定义PointEvaluator:

public valss PointEvaluator implements TypeEvaluator{
    @Overrride
    public Object evaluate(float fraction, Object startValue, Object endValue){
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        float x = startPoint + fraction*(endPoint.getX() - startPoint.getX());
        float y = startPoint + fraction*(endPoint.getY() - startPoint.getY());
        Point point = new Point(x, y);
        reutrn point;
    }
}

PointEvaluator同样实现了TypeEvaluator接口并重写了evaluate()方法。在evaluate()方法中的先将startValue和endValue强制转为Point对象,然后根据fraction来计算当前动画的x和y的值,然后封装到一个新的Point对象返回。
对Point对象进行动画操作:
新建一个MyAninView继承自View:

public class MyAnimView extends View{
    public static final float RADIUS = 50f;
    private Point currentPoint;
    private Paint mPaint;

    public MyAnimView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//用于绘制时消除锯齿
        mPaint.setColor(Color.BULE);
    }

    @Override
    protected void OnDraw(Canvas canvas){
        if (currentPoint == null){
            currentPoint = new Point(RADIUS , RADIUS );
            drawCircle(canvas);
            startAnimation();
        } else{
            drawCircle(canvas);
        }
    }
    private void drawCircle(Canvas canvas){
        float x = currentPoint.getX();
        float y = currentPoint.getY();
        canvas.drawCircle(x, y, RADIUS, mPaint);
    }
    private void startAnimation() {
        Point startPoint = new Point(RADIUS, RADIUS);
        Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
        valueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
            @Override
            public void onAnimationUpdate(ValueAnimator animation){
                currentPoint = (Point) animation.getAnimateValue();
                invalidate(); //请求重新draw(),但只会绘制调用者本身
            }
        });
        anim.setDuration(5000);
        anim.start();
    }
}

首先在自定义View的构造方法当中初始化了一个Paint对象作为画笔,并将画笔颜色设置为蓝色,接着在onDraw()方法当中进行绘制。这里我们绘制的逻辑是由currentPoint这个对象控制的,如果currentPoint对象不等于空,那么就调用drawCircle()方法在currentPoint的坐标位置画出一个半径为50的圆,如果currentPoint对象是空,那么就调用startAnimation()方法来启动动画。


  • 布局动画
    布局动画是指作用在ViewGroup上,给ViewGroup增加View时添加一个动画过度效果。
    最简单的布局动画是在IViewGroup的XML中,当ViewGroup添加View时,子View会呈现逐渐显示Android默认的过渡效果,:
android:animateLayoutChanges="true"

可以通过LayoutAnimationController类来自定义一个子View的过渡效果:

Linearlayout ll = (LinearLayout) findViewById(R.id.ll);
//设置过渡动画
ScaleAnimation sa  = new ScaleAnimation(0, 1, 0, 1);
sa.setDuration(2000);
//设置布局动画的显示属性
LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
//为ViewGroup设置布局动画
ll.setLayoutAnimation(lac);

通过以上代码,给LinearLayout增加了一个视图动画,让子View在出现的时候,有一个缩放的动画效果。
LayoutAnimationController构造函数的第一个参数是需要作用的动画,第二个参数是每个子View显示的delay时间。当delay不为0时,可以设置子View的顺序:

  • LayoutAnimationController.ORDER_NORMAL
  • LayoutAnimationController.ORDER_RANDOM 随机
  • LayoutAnimationController.ORDER_REVERSE 反序

  • Interpolator插值器
    这个方法主要是用来控制android动画的执行速率,可以使存在的动画效果:
  • AccelerateInterpolator ——在动画开始的地方速率改变比较慢,然后开始加速
  • AnticipateInterpolator ——开始的时候向后然后向前甩
  • AnticipateOvershootInterpolator ——开始的时候向后然后向前甩一定值后返回最后的值
  • BounceInterpolator ——开始时弹出,动画结束的时候弹起
  • CycleInterpolator ——动画循环播放特定的次数,速率改变沿着正弦曲线
  • DecelerateInterpolator—— 在动画开始的地方快然后慢
  • LinearInterpolator ——以常量速率改变
  • OvershootInterpolator ——向前甩一定值后再回到原来位置

相关文章

  • 属性动画学习使用总结

    Android 属性动画总结 属性动画的优点 属性动画的使用步骤 实例化属性动画对象 设置属性动画的时长 启动属性...

  • [Android动画]属性动画-小球下落动画实现

    属性动画 属性动画是通过直接改变View属性,实现的动画效果。与补间动画不同的是,属性动画是对象的属性的真实改变,...

  • Android动画-属性动画

    属性动画 所谓属性动画,就是改变对象Object的属性来实现动画过程。属性动画是对View的动画的扩展,通过它可以...

  • 动画之属性动画基础篇

    属性动画 属性动画,改变view或者object的属性实现动画,属性动画比补间动画更强大,不但可以实现旋转、平移等...

  • UI(四十八)属性动画

    属性动画->通过改变图层或者视图上面的属性值(支持动画的属性)产生的动画 属性动画的常用方法属性: 1、初始化 +...

  • iOS - 属性动画

    属性动画->通过改变图层或者视图上面的属性值(支持动画的属性)产生的动画 属性动画的常用方法属性: 1、初始化+(...

  • Android 动画

    动画类型 视图动画(补间动画、逐帧动画)属性动画 补间动画 逐帧动画 属性动画 对比 插值器:确定属性值从初始值过...

  • Android Animation 动画介绍与详解

    一、Animation 动画属性 动画相关的属性:SET属性 二、Animation 动画类型 Android的a...

  • 如何在网页中做出炫酷的动画(使用Spine)

    属性动画和帧动画 web中的动画主要分为属性动画和帧动画两种,属性动画是通过改变dom元素的属性如宽高、字体大小或...

  • 属性动画案例二(加载动画)

    继续属性动画,之前写过 属性动画案例一(基础动画与飘心动画) 简单了解了属性动画,这次来点炫酷的~加载动画,先看一...

网友评论

      本文标题:属性动画

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