Android Animation學習(三) ApiDemos解析:XML動畫文件的使用
可以用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