可以用XML文件来定义Animation。
文件必须有一个唯一的根节点:
<set>, <objectAnimator>, or <valueAnimator>三者之一。
对应的Java类是:
- ValueAnimator - <animator>
- ObjectAnimator - <objectAnimator>
- AnimatorSet - <set>
<set>标签是可以嵌套的。
<set>标签的android:ordering属性规定了这个set中的动画的执行顺序。该属性值默认是together (default)。
比如:
<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>
使用时:
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
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()); } } }
相关动画:
资源文件:
第一个小球:下落,并且返回:
<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"/>
第二个小球:消失(变为透明),然后再出现:
<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"/>
第三个小球:X轴与Y轴同时运动,并且返回:
<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>
第四个小球:颜色变化:
<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"/>
参考资料
API Guides:Declaring Animations in XML
项目地址:https://github.com/mengdd/AnimationApiDemos.git