• 沒有找到結果。

Time for action – making an animated Orrery

在文檔中 Android 3.0 Animations (頁 137-144)

An Orrery is a mechanical device that models the solar system. In the middle, there is the Sun, and around it spins planets, the Moon, and occasionally fixed stars. They were used in ancient Greece to aid navigation, and they have been used as an illustrative device by the sciences since the 1700s.

They used to be made out of metal, cogs, and gears. But today we will make one out of Drawables, Animators, and PropertyValuesHolders. You saw that one coming, didn't you?

Enough lollygagging, let's make it! The steps for making an Orrery are as follows:

1.

Create a new Android project and give it the following properties:

‰ Project name: Orrery

‰ Build Target: Android 3.0

‰ Application name: Orrery

‰ Package name: com.packt.animation.orrery

‰ Activity: Orrery

2.

First up, let's create the Orrery model itself. Create a new class in the project and call it OrreryDrawable.java

3.

In OrreryDrawable.java, add the following code. I will interrupt it occasionally to explain the structure, but only if it is going to help us understand the animation that we will make later.

package com.packt.animation.orrery;

import android.graphics.Color;

import android.graphics.Point;

4.

A few facts about our orrery: You will notice that there is only one planet in this one, and that's just to keep the example simple. The only things that change with time will be the rotations of the Earth and the Moon, which we store in degrees.

private static final int SPACE_HEIGHT = 150;

private static final int RADIUS_SUN = 20;

private static final int RADIUS_EARTH = 10;

private static final int RADIUS_MOON = 3;

private static final int ORBIT_EARTH = 50;

private static final int ORBIT_MOON = 20;

private static final int SPACE_ID = 0;

private static final int SUN_ID = 1;

private static final int EARTH_ID = 2;

private static final int MOON_ID = 3;

private float rotationEarth=0;

private float rotationMoon=0;

public static OrreryDrawable Create() {

5.

Our heavenly bodies will be defined as simple ShapeDrawables in the following code:

ShapeDrawable space = new ShapeDrawable(new RectShape());

space.getPaint().setColor(Color.BLACK);

space.setIntrinsicHeight(SPACE_HEIGHT);

space.setIntrinsicWidth(SPACE_HEIGHT);

ShapeDrawable sun = new ShapeDrawable(new OvalShape());

sun.getPaint().setColor(Color.YELLOW);

sun.setIntrinsicHeight(RADIUS_SUN*2);

sun.setIntrinsicWidth(RADIUS_SUN*2);

ShapeDrawable earth = new ShapeDrawable(new OvalShape());

earth.getPaint().setColor(Color.BLUE);

earth.setIntrinsicHeight(RADIUS_EARTH*2);

earth.setIntrinsicWidth(RADIUS_EARTH*2);

ShapeDrawable moon = new ShapeDrawable(new OvalShape());

moon.getPaint().setColor(Color.LTGRAY);

moon.setIntrinsicHeight(RADIUS_MOON*2);

moon.setIntrinsicWidth(RADIUS_MOON*2);

Drawable[] bodies = {space, sun, earth, moon};

OrreryDrawable myOrrery = new OrreryDrawable(bodies);

myOrrery.setEarthPosition(0);

myOrrery.setMoonPosition(0);

myOrrery.setLayerInset(

SPACE_ID,0,0,0,0);

myOrrery.setLayerInset(

SUN_ID,

private OrreryDrawable(Drawable[] bodies) {

super(bodies);

}

6.

Let us create some new methods to calculate the positions of the heavenly bodies as they move. The things that we are interested in are the rotational position of the Earth (expressed in radians because that's how the Java Math class expresses angles) and the rotational position of the Moon relative to the Earth (also expressed in radians).

public void setEarthPosition(float rotationEarth) {

this.rotationEarth = rotationEarth;

Point earthCenter = getEarthCenter();

setLayerInset(

EARTH_ID,

(int) (earthCenter.x - RADIUS_EARTH), (int) (earthCenter.y - RADIUS_EARTH),

(int) (SPACE_HEIGHT - earthCenter.x - RADIUS_

EARTH),

(int) (SPACE_HEIGHT - earthCenter.y - RADIUS_

EARTH));

7.

The next line, shown as follows, calls onBoundsChange on the LayerDrawable to refresh the child Drawables (the Earth, Sun, and so on.)

this.onBoundsChange(getBounds());

}

public void setMoonPosition(float rotationMoon) {

this.rotationMoon = rotationMoon;

Point moonCenter = getMoonCenter();

this.onBoundsChange(getBounds());

}

private Point getEarthCenter() {

Point earthCenter = new Point();

earthCenter.x =

(int) (SPACE_HEIGHT/2 + ORBIT_EARTH*Math.

sin(rotationEarth));

earthCenter.y =

(int) (SPACE_HEIGHT/2 + ORBIT_EARTH*Math.

cos(rotationEarth));

Point earthCenter = getEarthCenter();

moonCenter.x =

(int) (earthCenter.x + ORBIT_MOON*Math.sin(rotationMoon));

(int) (earthCenter.y + ORBIT_MOON*Math.cos(rotationMoon));

return moonCenter;

}

public void setContainer(ImageView orrery) container = orrery;

}

8.

We have now created two animation points within the same graphic.

9.

Okay, now we need to lay it out in the GUI. Open up res/layout/main.xml and set it up to look as follows:

<?xml version="1.0" encoding="utf-8"?>

10.

As you may guess from the previous code, the interesting part is the ImageView called "@+id/orrery". This is going to be the container object for our

actual orrery.

11.

As you may be expecting after all these tutorials, the next thing to change will be the Activity code. Open up Orrery.java and add the following modules to the imports: manipulate the image in main.xml. PropertyValuesHolder is a new value, and that's the point of this exercise. So let's begin and use it.

13.

At the end of the onCreate() method, we will attach our new animation to the ImageView, which we declared in main.xml. Add the following lines:

ImageView orrery = (ImageView) findViewById(R.id.orrery);

OrreryDrawable myOrreryDrawable = OrreryDrawable.Create();

orrery.setImageDrawable(myOrreryDrawable);

14.

This places our new animation on the screen, but we haven't added any animation properties yet.

15.

Let's take a look at it anyway, so that we can see what we're working towards. Build the project and deploy it to a device or emulator. The output is shown as follows:

16.

We only want to create one Animator, but we will use it to animate both the Earth and the Moon. Add the following lines:

PropertyValuesHolder earthPositionValues = PropertyValuesHolder.ofFloat(

"EarthPosition", 0,

(float)(2*Math.PI));

PropertyValuesHolder moonPositionValues = PropertyValuesHolder.ofFloat(

"MoonPosition", 0,

(float)(2*Math.PI*13));

17.

We have our parameters. Let's add them to an ObjectAnimator associated with the orrery drawable, shown as follows:

ObjectAnimator orreryAnimator = ObjectAnimator.ofPropertyValuesHolder(

myOrreryDrawable, earthPositionValues, moonPositionValues);

18.

The format is fairly simple; it's a bit like the ObjectAnimator.ofFloat() and ObjectAnimator.ofInt() that we used in the last chapter, but this time, we're adding the PropertyValuesHolders instead of a simple list of values. This is what allows us to apply more than one property at once.

19.

Next, to do some basic housekeeping. This should look familiar from the previous chapter too. Add the following lines:

ValueAnimator.setFrameDelay(100);

orreryAnimator.setDuration(60000);

orreryAnimator.setInterpolator(new LinearInterpolator());

orreryAnimator.setRepeatCount(ValueAnimator.INFINITE);

orreryAnimator.setRepeatMode(ValueAnimator.RESTART);

20.

This should all be recognizable from earlier, but check out the following What just happened? section if you need a little revision.

21.

Finally, right at the end of onCreate(), add the following line to start the animation:

orreryAnimator.start();

22.

Just like the previous animators!

23.

Now, our orrery is ready for use. Build and run it. The output is shown as follows:

What just happened?

Here we saw yet another approach to animation, where a single animation controls several properties. Each PropertyValuesHolder modified exactly one property, but the animation itself could have multiple PropertyValuesHolders. This is very useful when there are lots of things that you want to animate as one conceptual unit.

Please note that the simplicity of the application has meant that some of the more precise features of an orrery have had to be simplified. Please don't attempt to use this orrery for navigation; you will end up getting very, very lost!

在文檔中 Android 3.0 Animations (頁 137-144)