属性动画

2016-11-07  本文已影响0人  A_Coder

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


ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);  
anim.setDuration(300);  
anim.start()

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


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()方法的第二个参数可以是

public void setAlpha(float value);  
public float getAlpha();

示例:
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();

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){ //在动画结束的时候调用
    }
});

<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()
            .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();

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用于表示动画的完成度的,根据它来计算当前动画的值应该是多少,第二第三个参数分别表示动画的初始值和结束值。

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()方法来启动动画。


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的顺序:



上一篇 下一篇

猜你喜欢

热点阅读