MobAppDev (Fall 2014): Touch Driving Drawings & Sprites

Post on 22-Jun-2015

121 views 1 download

Tags:

transcript

MobAppDev

Touch Driving Drawings & Sprites

Vladimir Kulyukin

www.vkedco.blogspot.com

Outline● Review● Touch Driving Drawings● Touch Driving Sprites

Brief Review

MotionEvent● On Android, digital data from touchscreens are captured as

MotionEvent objects● MotionEvent objects are created when the user touches the device's

touchscreen● Each MotionEvent object contains the x and y coordinates (and

some other stats) of the captured touch● MotionEvent objects are handled by the View.onTouchEvent()

method

MotionEvent Sequences

● When user places his/her finger, moves it (without lifting it up), and then lifts it up, a sequence of MotionEvent objects is generated

● Such sequences can be captured and used in touch gesture recognition

● Each MotionEvent object contains information on: 1) type of action captured (e.g., MotionEvent.ACTION_DOWN, MotionEvent.ACTION_UP, etc); 2) pressure value; 3) x and y coordinates; 4) time of event

MotionEvent.getAction()

● MotionEvent.getAction() can be used to retrieve the type of action● Examples:

MotionEvent.getAction() returns ACTION_DOWN when user touches screen

MotionEvent.getAction() returns ACTION_MOVE when user moves sideways

MotionEvent.getAction() returns ACTION_UP when user lifts his/her finger

Interface View.OnTouchListener

● Classes the receive touchscreen events must implement View.OnTouchListener

● Two main methods to handle MotionEvents are: onTouch(View, MotionEvent) onTouchEvent(MotionEvent)

● onTouch(View, MotionEvent) is used when one OnTouchListener handles MotionEvents from multiple views

Touch Driving Drawings

Definition of Touch Driving

Touch driving is a touchscreen technique that allows the user to drive drawings and/or images (sprites) with his/her finger.

Problem

Develop an application that draws a 3x3 Tic Tac Toe Board, 2 red circles, and 2 blue crosses. The user can touch drive circles and crosses on the screen.

Source code of DrawTouchDrive app here

Screenshots

Initial Screenshot Touch Driving a Circle Touch Driving a Cross

Solution Outline

● Define a custom View object that extends the View class● This custom View class (TicTacToeView.java) handles

all touch events and does all drawing● All basic board geometry is abstracted in a separate

class (BoardGeometry.java)

X & Y Offsets of a Circle DrawingX

y

ME's X

ME's Y

X Offset

Y Offset

final float me_x = me.getX();

final float me_y = me.getY();

final int action = me.getAction();

c.setActionDownX(c.getCurrentX());

c.setActionDownY(c.getCurrentY());

c.setActionMoveOffsetX(me_x);

c.setActionMoveOffsetY(me_y);

final float me_x = me.getX();

final float me_y = me.getY();

final int action = me.getAction();

c.setActionDownX(c.getCurrentX());

c.setActionDownY(c.getCurrentY());

c.setActionMoveOffsetX(me_x);

c.setActionMoveOffsetY(me_y);

me is a MotionEvent

c is a Circle

Custom View: TicTacToeView<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent" >

<org.vkedco.mobappdev.draw_touch_drive_00001.TicTacToeView

android:id="@+id/pntr"

android:tag="Painter"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

</RelativeLayout>

TicTacToeView.euclidDistance()

// TicTacToeView uses Euclidean Distance to find closest objects

private static float euclidDist(float x1, float y1, float x2, float y2)

{

return android.util.FloatMath.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));

}

TicTacToeView: Member Variablespublic class TicTacToeView extends View {

// define background and foreground paints

final Paint mBackgroundPaint;

final Paint mForeCirclePaint;

final Paint mForeCrossPaint;

final Paint mForeBoardPaint;

// define a BoardGeometry object

final BoardGeometry mBoardGeometry;

// define arrays of circles and crosses

ArrayList<Circle> mCircles;

ArrayList<Cross> mCrosses;

}

TicTacToeView: Construction of Circles & Crossespublic class TicTacToeView extends View {

// placing circles and crosses to their start x's & y's

private void createCircles() {

mCircles = new ArrayList<Circle>();

mCircles.add(new Circle(30, 30, 20));

mCircles.add(new Circle(120, 30, 20));

}

private void createCrosses() {

mCrosses = new ArrayList<Cross>();

mCrosses.add(new Cross(30, 350, 20));

mCrosses.add(new Cross(120, 350, 20));

}

}

public class TicTacToeView extends View {

// placing circles and crosses to their start x's & y's

private void createCircles() {

mCircles = new ArrayList<Circle>();

mCircles.add(new Circle(30, 30, 20));

mCircles.add(new Circle(120, 30, 20));

}

private void createCrosses() {

mCrosses = new ArrayList<Cross>();

mCrosses.add(new Cross(30, 350, 20));

mCrosses.add(new Cross(120, 350, 20));

}

}

Handling MotionEvents

● Get the x and y coordinates of a MotionEvent ME● Find a circle C closest to ME● Find a cross X closest to ME● If the Euclidean distance from ME to the closest circle C is smaller than

the Euclidean distance from ME to the closest cross X, then assume that the circle C was touched and handle it (redraw it at ME's x and y); otherwise, assume that the cross was touched and handle it

TicTacToeView: Handling MotionEventspublic boolean onTouchEvent(MotionEvent event) {

// 1. get the x and y of MotionEvent

float x = event.getX(); float y = event.getY();

// 2. find circle closest to x and y

Circle cr = findCircleClosestToTouchEvent(x, y);

// 3. find cross closest to x and y

Cross cx = findCrossClosestToTouchEvent(x, y);

// 4. compute euclid distances to find which is

// closer - circle or cross

float dtcr = euclidDist(cr.getCurrentX(), cr.getCurrentY(), x, y);

float dtcx = euclidDist(cx.getMidX(), cx.getMidY(), x, y);

// 5. if distance to closest circle is smaller

// handle the circle; otherwise, handle the cross

if (dtcr < dtcx) { handleTouchedCircle(event, cr); }

else { handleTouchedCross(event, cx); }

return true;

}

Drawing the Canvas● Draw the background rectangle over the entire canvas● Draw the TicTacToe board (horizontal and vertical lines)● Draw the circles● Draw the crosses● Invalidate the canvas object to force it to redraw

TicTacToeView: Canvas Drawing public void draw(Canvas canvas) {

if ( mDrawingEnabled ) {

final int width = canvas.getWidth(); final int height = canvas.getHeight();

// 1. draw background rectangle that covers the entire canvas

canvas.drawRect(0, 0, width, height, mBackgroundPaint);

// 2. draw board on the canvas

drawBoard(canvas);

// 3. draw red circles on canvas

drawCirclesOnCanvas(canvas);

// 4. draw blue crosses on canvas

drawCrossesOnCanvas(canvas);

// 5. force redraw

invalidate();

}

}

Touch Driving Sprites

Problem

Develop an application that displays 3 black chess pieces above and 3 white chess pieces below. The application allows the user to touch drive the black chess pieces down. If a black chess piece is driven close to a white chess piece, the white chess piece jumps on the black chess piece. The user's objective is to touch drive the black pieces down.

Source code of ImageTouchDrive is here

Screenshots

Initial Screen Piece Capture End Game

Sprites

Black Bishop

Black King

Black Knight

White Bishop

White King

White Knight

I have taken these images from http://en.wikipedia.org/wiki/Chess

ChessPiecepublic class ChessPiece {

final Bitmap mBitmap; // image of chess piece

float mStartX; // x of top left corner of bitmap

float mStartY; // y of top left corner of bitmap

float mCurrentX; // current x coordinate of ChessPiece

float mCurrentY; // current y coordinate of ChessPiece

float mActionDownX; // x coordinate of ChessPiece of an action down

float mActionDownY; // y coordinate of ChessPiece of an action down

float mActionMoveOffsetX; // x coordinate of a move action

float mActionMoveOffsetY; // y coordinate of a move action

float mEuclidDistThresh; // threshold to decide if motion event should be consumed

// rest of code

}

ChessPiece Construction

public ChessPiece(Resources res, int res_id, float dthresh, float start_x, float start_y) {

mBitmap = BitmapFactory.decodeResource(res, res_id);

mStartX = start_x;

mStartY = start_y;

mCurrentX = start_x;

mCurrentY = start_y;

mEuclidDistThresh = dthresh;

}

ChessPiece's Handling of Touch Events

void handleOnTouchEvent(MotionEvent me) {

final float me_x = me.getX(); final float me_y = me.getY();

final float left_x = mCurrentX; final float top_y = mCurrentY;

final float right_x = left_x + (float)mBitmap.getWidth();

final float bot_y = mCurrentY + (float)mBitmap.getHeight();

}

ChessPiece's Handling of Touch Events

void handleOnTouchEvent(MotionEvent me) {

if ( euclidDist(left_x, top_y, me_x, me_y) > mEuclidDistThresh &&

euclidDist(right_x, top_y, me_x, me_y) > mEuclidDistThresh &&

euclidDist(left_x, bot_y, me_x, me_y) > mEuclidDistThresh &&

euclidDist(right_x, bot_y, me_x, me_y) > mEuclidDistThresh )

{ return; }

}

ChessPiece's Handling of Touch Events

void handleOnTouchEvent(MotionEvent me) {

final int action = me.getAction();

switch ( action ) {

case MotionEvent.ACTION_DOWN:

this.setActionDownX(this.getCurrentX()); this.setActionDownY(this.getCurrentY());

this.setActionMoveOffsetX(me_x); this.setActionMoveOffsetY(me_y); break;

case MotionEvent.ACTION_MOVE:

case MotionEvent.ACTION_UP:

this.setCurrentX(this.getActionDownX() + me_x - this.getActionMoveOffsetX());

this.setCurrentY(this.getActionDownY() + me_y – this.getActionMoveOffsetY()); break;

case MotionEvent.ACTION_CANCEL: this.restoreInitialPosition(); break;}

}

References ● en.wikipedia.org/wiki/Touchscreen

● http://developer.android.com/reference/android/view/MotionEvent.html

● http://developer.android.com/reference/android/view/View.OnTouchListener.html

● http://en.wikipedia.org/wiki/Chess