|
Page 2 of 2
The interesting stuff is in the handler definition within the CustomView inner class. When a message arrives instructing it to recalculate the babies positions, it walks through their position list and tests each one to see is it is already in position. If they all are, no further messages are sent. If even just one isn't, the message is sent to update again when all the babies have been processed.
One word of caution - this example is purely to show the message queueing system and is not the way you'd normally write an app to solve this particular ducks-in-a-row problem - that would be just quackers ;-)
/*
* Copyright (c) 2009 OTAMate Technology Ltd. All Rights Reserved.
* http://www.androidacademy.com
*/
package com.androidacademy.tutorials.messagequeue;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Region;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;
/**
* This shows how to update the UI in response to messages.
* A duck pond is drawn, and on a finger press event momma duck is placed
* along with a few smaller randomly positioned baby ducks. The baby ducks
* will always try to line up behind momma duck. If they are not in position,
* they know of it because momma duck sends them all a message each time
* she moves. Every movement from a baby duck is therefore in response
* to a message.
*/
public class MessageQueueActivity extends Activity {
/**
* Handle the onCreate event
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DuckPondView(this));
}
/**
* Create a DuckPondView which draws the duck pond and the ducks, and
* handles the finger press events by sending and handling the appropriate
* messages.
*/
private static class DuckPondView extends View {
private int mBabyDuckCount;
private static final int MSG_RECALC_BABYDUCK_POSITIONS = 1;
private static final int BABY_UPDATE_TICKTIME = 200;
private final Paint mPaint = new Paint();
private Bitmap mBmDuckPond;
private Bitmap mBmMommaDuck;
private Bitmap mBmBabyDuck;
private int mMommaDuckXPos = -1;
private int mMommaDuckYPos = -1;
private List<Point> mBabyDuckPositions = new ArrayList<Point>();
public DuckPondView(Context context) {
super(context);
setFocusable(true);
// Create the bitmaps
mBmDuckPond = BitmapFactory.decodeResource(getResources(),
R.drawable.duckpond);
mBmMommaDuck = BitmapFactory.decodeResource(getResources(),
R.drawable.mommaduck);
mBmBabyDuck = BitmapFactory.decodeResource(getResources(),
R.drawable.babyduck);
// Place the baby ducks randomly
Random generator = new Random();
mBabyDuckCount = 5 + generator.nextInt(3);
for (int i = 0; i < mBabyDuckCount; i++) {
Point position = new Point ( 50 + generator.nextInt(250),
50 + generator.nextInt(350) );
mBabyDuckPositions.add(position);
}
}
/**
* The main duck pond draw handler
*/
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBmDuckPond, 0, 0, mPaint);
// Draw Momma duck if she's present
if (!(mMommaDuckXPos < 0 && mMommaDuckYPos < 0)) {
canvas.drawBitmap(mBmMommaDuck, mMommaDuckXPos, mMommaDuckYPos, mPaint);
}
// Draw the baby ducks
for (Point position: mBabyDuckPositions) {
canvas.drawBitmap(mBmBabyDuck, position.x, position.y, mPaint);
}
}
/**
* Handle the finger press events
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (mMommaDuckXPos < 0 && mMommaDuckYPos < 0) {
// No Momma duck present so set it
mMommaDuckXPos = (int) event.getX() -
(mBmMommaDuck.getWidth() / 2);
mMommaDuckYPos = (int) event.getY() -
(mBmMommaDuck.getHeight() / 2);
}
invalidate();
break;
}
case MotionEvent.ACTION_MOVE: {
// Was this a press within the Momma Duck Region?
if (!(mMommaDuckXPos < 0 && mMommaDuckYPos < 0)) {
Region r = new Region(mMommaDuckXPos, mMommaDuckYPos,
mMommaDuckXPos + mBmMommaDuck.getWidth(),
mMommaDuckYPos + mBmMommaDuck.getHeight());
if (r.contains((int) event.getX(), (int) event.getY())) {
mMommaDuckXPos = (int) event.getX() -
(mBmMommaDuck.getWidth() / 2);
mMommaDuckYPos = (int) event.getY() -
(mBmMommaDuck.getHeight() / 2);
}
Message msg = Message.obtain();
msg.arg1 = MSG_RECALC_BABYDUCK_POSITIONS;
handler.sendMessageDelayed(msg, BABY_UPDATE_TICKTIME);
}
invalidate();
break;
}
}
return true;
}
/**
* Handle incoming messages
*/
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.arg1) {
case MSG_RECALC_BABYDUCK_POSITIONS: {
// Iterate through the duck position List checking
// each one is in position. If any are not, recalculate
// their positions. If they are all in position, set
// the allInPosition flag because it means no more
// recalc messages are needed.
boolean allInPosition = true;
boolean thisDuckHasMoved;
int duckCount = 0;
int finalDuckXPos;
int finalDuckYPos;
int newDuckXPos;
int newDuckYPos;
for (Point position: mBabyDuckPositions) {
thisDuckHasMoved = false;
// Calc where duck should be
finalDuckXPos = mMommaDuckXPos
+ mBmMommaDuck.getWidth()
+ (duckCount * mBmBabyDuck.getWidth());
finalDuckYPos = mMommaDuckYPos
+ (mBmMommaDuck.getHeight() / 2);
// Is this duck in position?
// Do we need to move this duck in the X axis?
if (finalDuckXPos != position.x) {
thisDuckHasMoved = true;
newDuckXPos = position.x;
if (finalDuckXPos < position.x) {
newDuckXPos--;
} else {
newDuckXPos++;
}
position.x = newDuckXPos;
}
// Do we need to move this duck in the Y axis?
if (finalDuckYPos != position.y) {
thisDuckHasMoved = true;
newDuckYPos = position.y;
if (finalDuckYPos < position.y) {
newDuckYPos--;
} else {
newDuckYPos++;
}
position.y = newDuckYPos;
}
if (thisDuckHasMoved) {
mBabyDuckPositions.set(duckCount, position);
allInPosition = false;
}
duckCount++;
}
if (!allInPosition) {
Message msgUpdate = Message.obtain();
msgUpdate.arg1 = MSG_RECALC_BABYDUCK_POSITIONS;
handler.sendMessageDelayed(msgUpdate,
BABY_UPDATE_TICKTIME);
invalidate();
}
break;
}
}
}
};
}
}
|