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)
Object-Oriented Modeling
● BoardGeometry.java ● Shape.java ● Circle.java ● Cross.java ● TicTacToeView.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
Top Related