Android Animation学习(三) ApiDemos解析:XML动画文件的使用

可以用XML文件来定义Animation。

  文件必须有一个唯一的根节点:

  <set>, <objectAnimator>, or <valueAnimator>三者之一。

  对应的Java类是:

  <set>标签是可以嵌套的。

  <set>标签的android:ordering属性规定了这个set中的动画的执行顺序。该属性值默认是together (default)。

  比如:

Android Animation学习(三) ApiDemos解析:XML动画文件的使用
<set android:ordering="sequentially" >

    <set>
        <objectAnimator
            android:duration="500"
            android:propertyName="x"
            android:valueTo="400"
            android:valueType="intType" />
        <objectAnimator
            android:duration="500"
            android:propertyName="y"
            android:valueTo="300"
            android:valueType="intType" />
    </set>

    <objectAnimator
        android:duration="500"
        android:propertyName="alpha"
        android:valueTo="1f" />

</set>
Android Animation学习(三) ApiDemos解析:XML动画文件的使用

 

  

  使用时:

        AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(
                myContext, R.anim.property_animator);
        set.setTarget(myObject);
        set.start();


  为了区分Property animation和View animation的资源文件,从Android 3.1开始,Property animation的xml文件存在res/animator/目 录下(View animation的存在res/anim/目录下), animator这个名是可选的。但是如果你想要使用Eclipse ADT plugin (ADT 11.0.0+)的布局编辑器,你就必须使用res/animator/目录,因为ADT只在该目录下寻找property animation的资源文件。

Api Demo相关代码:

  代码结构和上一篇文章中的基本类似,也是各种小球的动画,只不过这次的动画效果都是从XML文件中读取的。

  完整的项目见项目地址:https://github.com/mengdd/AnimationApiDemos.git

Android Animation学习(三) ApiDemos解析:XML动画文件的使用
public class AnimationFromXmlActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 设置布局,布局xml中只包含了一个线性布局和一个Button
        setContentView(R.layout.animation_basic);

        LinearLayout container = (LinearLayout) findViewById(R.id.container);

        // 将自定义的View加入到线性布局中
        final MyAnimationView animView = new MyAnimationView(this);
        container.addView(animView);

        // Button的点击事件即动画开始
        Button starter = (Button) findViewById(R.id.startButton);
        starter.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {
                animView.startAnimation();
            }
        });
    }

    public class MyAnimationView extends View implements
            ValueAnimator.AnimatorUpdateListener {

        private static final float BALL_SIZE = 100f;

        public final ArrayList<ShapeHolder> balls = new ArrayList<ShapeHolder>();
        Animator animation = null;

        public MyAnimationView(Context context) {
            super(context);
            addBall(50, 50);
            addBall(200, 50);
            addBall(350, 50);
            addBall(500, 50, Color.GREEN);
        }

        private void createAnimation() {
            Context appContext = AnimationFromXmlActivity.this;

            if (animation == null) {

                // ========================================================
                // 载入根节点为<objectAnimator>的xml资源文件,解析放进ObjectAnimator类对象
                ObjectAnimator anim = (ObjectAnimator) AnimatorInflater
                        .loadAnimator(appContext, R.anim.object_animator);
                anim.addUpdateListener(this);
                anim.setTarget(balls.get(0));

                // ========================================================
                // 载入根节点为<animator>的xml资源文件,解析放进ValueAnimator类对象
                ValueAnimator fader = (ValueAnimator) AnimatorInflater
                        .loadAnimator(appContext, R.anim.animator);
                fader.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    public void onAnimationUpdate(ValueAnimator animation) {
                        invalidate();
                        // ValueAnimator动画需要在监听器中自己设置对象的属性值
                        // 这里改变的是alpha值
                        balls.get(1).setAlpha(
                                (Float) animation.getAnimatedValue());
                    }
                });

                // ========================================================
                // 载入根节点为<set>的xml资源文件,解析放进AnimatorSet类对象
                AnimatorSet seq = (AnimatorSet) AnimatorInflater.loadAnimator(
                        appContext, R.anim.animator_set);// x和y属性同时改变的动画集合
                seq.setTarget(balls.get(2));
                // 这里要注意:因为AnimatorSet没有设置AnimatorUpdateListener的方法,
                // 所以如果其他动画没有设置AnimatorUpdateListener来进行View的invalidate()刷新,
                // 这个AnimatorSet seq是不刷新的

                // ========================================================
                // 载入根节点为<objectAnimator>的xml资源文件,解析放进ObjectAnimator类对象
                ObjectAnimator colorizer = (ObjectAnimator) AnimatorInflater
                        .loadAnimator(appContext, R.anim.color_animator);
                colorizer.setTarget(balls.get(3));
                colorizer.addUpdateListener(this);

                // ========================================================
                // 总的AnimationSet,所有的动画同时播放
                animation = new AnimatorSet();
                ((AnimatorSet) animation).playTogether(anim, fader, seq,
                        colorizer);
            }
        }

        public void startAnimation() {
            createAnimation();
            animation.start();
        }

        private ShapeHolder createBall(float x, float y) {
            OvalShape circle = new OvalShape();
            circle.resize(BALL_SIZE, BALL_SIZE);
            ShapeDrawable drawable = new ShapeDrawable(circle);
            ShapeHolder shapeHolder = new ShapeHolder(drawable);
            shapeHolder.setX(x);
            shapeHolder.setY(y);
            return shapeHolder;
        }

        private void addBall(float x, float y, int color) {
            ShapeHolder shapeHolder = createBall(x, y);
            shapeHolder.setColor(color);
            balls.add(shapeHolder);
        }

        private void addBall(float x, float y) {
            ShapeHolder shapeHolder = createBall(x, y);
            int red = (int) (100 + Math.random() * 155);
            int green = (int) (100 + Math.random() * 155);
            int blue = (int) (100 + Math.random() * 155);
            int color = 0xff000000 | red << 16 | green << 8 | blue;
            Paint paint = shapeHolder.getShape().getPaint();
            int darkColor = 0xff000000 | red / 4 << 16 | green / 4 << 8 | blue
                    / 4;
            RadialGradient gradient = new RadialGradient(37.5f, 12.5f, 50f,
                    color, darkColor, Shader.TileMode.CLAMP);
            paint.setShader(gradient);
            balls.add(shapeHolder);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            // 遍历并绘制每一个球形对象
            for (ShapeHolder ball : balls) {
                // 这里是canvas.translate到一个地方,进行绘制,之后再translate回来
                // 跟先save后restore的作用相同
                canvas.translate(ball.getX(), ball.getY());
                ball.getShape().draw(canvas);
                canvas.translate(-ball.getX(), -ball.getY());
            }
        }

        public void onAnimationUpdate(ValueAnimator animation) {

            // 刷新View
            invalidate();

            // 因为第一个小球用的是ObjectAnimator,所以这里不必要自己设置属性值
            // 如果是ValueAnimator就需要加上下面两行
            // ShapeHolder ball = balls.get(0);
            // ball.setY((Float) animation.getAnimatedValue());
        }
    }
}
Android Animation学习(三) ApiDemos解析:XML动画文件的使用

相关动画:

  资源文件:

Android Animation学习(三) ApiDemos解析:XML动画文件的使用

  第一个小球:下落,并且返回:

Android Animation学习(三) ApiDemos解析:XML动画文件的使用
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueTo="200"
    android:valueType="floatType"
    android:propertyName="y"
    android:repeatCount="1"
    android:repeatMode="reverse"/>
Android Animation学习(三) ApiDemos解析:XML动画文件的使用

  第二个小球:消失(变为透明),然后再出现:

Android Animation学习(三) ApiDemos解析:XML动画文件的使用
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueFrom="1"
    android:valueTo="0"
    android:valueType="floatType"
    android:repeatCount="1"
    android:repeatMode="reverse"/>
Android Animation学习(三) ApiDemos解析:XML动画文件的使用

  第三个小球:X轴与Y轴同时运动,并且返回:

Android Animation学习(三) ApiDemos解析:XML动画文件的使用
<set>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:valueTo="200"
        android:valueType="floatType"
        android:propertyName="x"
        android:repeatCount="1"
        android:repeatMode="reverse"/>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:valueTo="400"
        android:valueType="floatType"
        android:propertyName="y"
        android:repeatCount="1"
        android:repeatMode="reverse"/>
</set>
Android Animation学习(三) ApiDemos解析:XML动画文件的使用


  第四个小球:颜色变化:

Android Animation学习(三) ApiDemos解析:XML动画文件的使用
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueFrom="#0f0"
    android:valueTo="#00ffff"
    android:propertyName="color"
    android:repeatCount="1"
    android:repeatMode="reverse"/>
Android Animation学习(三) ApiDemos解析:XML动画文件的使用

 

参考资料

  API Guides:Declaring Animations in XML

  Animation Resources

  项目地址:https://github.com/mengdd/AnimationApiDemos.git