Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Android™ 101: How to
draw on a SurfaceView
and handle touch
events.
Richard A. Perez
Focus: Handling user screen touch while drawing on a canvas.
Android 101
Table of Contents
Introduction .................................................................................................................................................. 3
Android Manifest ......................................................................................................................................... 4
Package ..................................................................................................................................................... 4
Version ...................................................................................................................................................... 4
Uses SDK.................................................................................................................................................... 4
Declare starting activity ............................................................................................................................ 5
Resources...................................................................................................................................................... 5
Drawable ................................................................................................................................................... 5
Layout........................................................................................................................................................ 5
Menu ......................................................................................................................................................... 6
Values ........................................................................................................................................................ 6
Anim .......................................................................................................................................................... 7
Main Activity ................................................................................................................................................ 8
OnCreate ................................................................................................................................................... 8
OnCreateOptionsMenu............................................................................................................................. 8
OnOptionsItemSelected............................................................................................................................ 8
OnResume ................................................................................................................................................. 9
Drawing Surface ......................................................................................................................................... 10
IncomingHandlerCallback ....................................................................................................................... 10
Constructor ............................................................................................................................................. 10
SurfaceCreated........................................................................................................................................ 10
SurfaceChanged ...................................................................................................................................... 11
SurfaceDestroyed.................................................................................................................................... 11
Draw ........................................................................................................................................................ 11
UpdatePhysics ......................................................................................................................................... 11
OnTouchEvent......................................................................................................................................... 11
SetTextView ............................................................................................................................................ 12
OnWindowFocusChanged....................................................................................................................... 13
MenuRestart ........................................................................................................................................... 13
OnResume ............................................................................................................................................... 13
OnPause .................................................................................................................................................. 13
Target Thread ............................................................................................................................................. 13
mRun ....................................................................................................................................................... 14
mMode.................................................................................................................................................... 14
Run .......................................................................................................................................................... 14
SetState ................................................................................................................................................... 14
Targeting ..................................................................................................................................................... 15
Targeting Class ........................................................................................................................................ 15
The Factory Pattern ................................................................................................................................ 15
Draw ........................................................................................................................................................ 16
MyRect .................................................................................................................................................... 16
SurfaceChanged ...................................................................................................................................... 17
OnTouch .................................................................................................................................................. 17
UpdatePhysics ......................................................................................................................................... 18
InitMyRects ............................................................................................................................................. 20
MenuRestart ........................................................................................................................................... 20
Testing Touch.............................................................................................................................................. 20
Import Git ................................................................................................................................................... 21
Attribution .................................................................................................................................................. 23
Introduction
This document is intended for developer’s who have some experience in setting up a development
environment and are familiar on an introductory level with the Java programming language. I have
created a project and commented the classes’, methods and member groupings in hopes it will simplify
the learning of Android™ and its lifecycle events. I have tried to include as many best practices
encouraged by Android as I can but you will need to read their official developer guide for a full
understanding. Many applications fail when the Activity lifecycle isn’t fully understood.
I created a project for you as a sample of what Android can do with relatively little coding. It is a
simple interactive application that I have developed that will show you how to start manipulating pixels
on a canvas. Also, as well as drawing on the SurfaceView object I include details on how to handle touch
events and include methods for providing accessibility for users with special needs.
I also have setup a Github account, a source control management site, and am hosting code from
there that will be useful when using these samples on your own. You should view or download the full
source code that is found in this guide from this link.
https://github.com/rperez22
Throughout the document I’ll add in shortcuts and hotkeys that are useful. If you still rely on your
mouse, it’s a good practice to start using and memorizing hotkeys to increase your productivity. The
phrase “Mouse Fatigue” is real and you’ll find that simply moving your hand from the keyboard to the
mouse over and over will get more tiring over time than using hotkeys to move windows or opening and
closing files and directories. Don’t make programming any harder than it already is!
After creating a new Android project a couple of folders of interest are src and res, here your source
code and resources, such as bitmaps and sound files, are placed. The AndroidManifest.xml file is a
declaration file for the starting Activity and permissions. The strings.xml in the values folder should be
used for any strings, where the value will not change. There is also an ic_launcher_web.png file that can
be used for the application icon in the developer console page when you are ready to make the app
listing with Google™.
The guide itself is intended to be a complete overview of the sample project I have provided for you.
I start with the AndroidManifest.xml file which declares the project package (unique id) and the versions
of the Android SDK that will be required of the user’s phone. The “res” or resources folder is where your
images and icons are placed as well the layout, menu, and static strings.xml file. The “src” code sections
contains all of our source code.
The intermediate topic to this guide is the use the use of the Factory Pattern design pattern. I use
this to create and return classes that will perform the updating of game physics, and drawing to the
canvas. The logic of both, will change as the state of the application progresses. In this sample we have
two implementations for each. One for the running state and one for the end state. We only need to use
one of them at one time. The Factory pattern is useful for switching out these classes, dynamically if
needed, based on the state of the application. Learning and using the Factory pattern will help keep
your application extensible by executing different code implementations to methods that share identical
signatures while separating the actual object creation from its usage.
Android Manifest
The AndroidManifest.xml file is where the application package is declared and you can set
permissions for the application to implement use of the internet, memory cards, and other features.
http://developer.android.com/guide/topics/manifest/manifest-intro.html
Package
The package here is “touch.target”. This declaration means that if this application were to be
listed on the Google Play™ store today you would see “touch.target” at the end of the URL link on the
homepage of the application. Also it’s the unique identifier for the application and must be unique
across the entire Google Play store listings. This is a little misleading though, as you don’t actually need
to have the package in your src. It’s simply declared in the manifest.
package="touch.target"
Version
Every APK (Android Application Package) you generate with the intention of having hosted on
Google Play will need to have a version number tied to it. You will need to increment the version code
and version name. Your APK’s are stored in numerical fashion and will need to be numbered correctly in
order to have the latest release the one on the storefront whether in alpha, beta or production.
android:versionCode="1"
android:versionName="1.0"
Uses SDK
Out of the box Android will suggest a minimum and a target SDK, but code to your needs to get
the task done. If raising the minimum version is needed for an SDK API method call or object then you
can traverse to the Android dashboard and see what percent the missing version has in the marketplace
and decide if missing that percentage of the possible users is worth the code you are implementing or if
it would be worth to find a different way to do the same task and keep the minSDKVersion as low as
possible.
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
https://developer.android.com/about/dashboards/index.html
Declare starting activity
Android can have multiple activities but one has to have the Main Intent filter declared inside of
it so you can declare the starting Activity of the application.
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Resources
Drawable
In the drawable folders, the ic_launcher.png icons are placed. These are the phone launch icons
the user will see on their device. Using Eclipse™ you can edit
Eclipse Create New Icon Set
icons in place if you want to, usually I open with windows viewer
and open the image in Paint.net but you can of course edit
1. Alt-F
however you like. You can create a drawable-nodpi for images to
2. New
3. Other
be used but not be scaled by Android on use. You can also create
4. “Android Icon Set”
a new icon set at any time followin the steps on the right.
Layout
Default layout is the activity_main.xml layout file. Click onto activity_main.xml tab at the bottom
of the IDE for the GUI create view. Here you can see all the controls you can add into an application and
change the settings of them. You can click on the activity_main.xml tab near the bottom of the Eclipse
IDE to see or edit the actual XML. You can start to add widgets to see the layout on your device you just
won’t have access to the functionality until you implement it.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/merge"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<touch.target.surface.DrawingSurface
android:id="@+id/drawing_surface"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
Once you have layout objects solidified such as buttons or text fields then the layout should be
independent of the actual classes that implements the logic. This means you can then work on
perfecting the implementation and perfecting the layout separately the bonus being that you can have
two people do it separately.
<RelativeLayout
android:id="@+id/relativetext"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="@+id/pause_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal"
android:text="@string/message_text"
android:textColor="#000000"
android:textSize="24sp"
android:textStyle="italic"
android:visibility="visible" />
</RelativeLayout>
</merge>
Menu
You can declare your menu items in a menu.xml layout file like this. Declaring the id for handling
on press, the icon for the item to have if the phone version supports it and the string reference to the
title of the menu item.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/restart"
android:icon="@drawable/ic_launcher"
android:title="@string/restart"/>
</menu>
Values
Commonly used in here is the strings.xml class, where you should store all your static strings
whose value will not change throughout the course of the applications lifecycle.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Touch Demo</string>
<string name="message_text">Tap Screen when boxes\nare
overlapping</string>
<string name="restart">Restart?</string>
</resources>
Anim
You can store your animation xml files in a folder titled “anim” here you store your xml files with
the animations you will be using for view objects. Used modestly, animations can enhance an
application with a splash screen or other visual cue to keep the user involved. Our project extends the
SurfaceView class and that class extends view, this allows us to add animation to it.
For the intro screen of this application, I take the TextView object that shows the title and start
by the animation by expanding it and, then as it fades into view, collapses it to its regular size. I
manipulate two effects here the scale effect, which is simply resizing of the image and the alpha for
transparency. The fromXScale is the starting point, 2 is the starting location, and the toXScale is 1. This
means that the view object will start out expanded and reduce to the normal view, we can set the pivot
or the point of focus the view expands from in the pivotX and pivotY locations. Here it’s centered so its
50% height and 50% width.
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true" >
<scale
android:duration="900"
android:fromXScale="2"
android:fromYScale="2"
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
<alpha
android:duration="900"
android:fromAlpha="0.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="1.0" />
</set>
The alpha effect is declared next and is a transparency effect. The duration is for how long in
milliseconds the effect will run for. The alpha (transparency) transition is for a nice dissolve in effect.
The interpolator is the style of the animation effect. In the scale I use a linear interpolator so it animates
the view object at a constant rate. For the alpha I use the accelerate_interpolator, this makes the
dissolve effect increase over time. Refer to the Animation documents in the official Android developer
tutorials.
Main Activity
The Main Activity is the starting point for the coding in the application and it extends an Activity
class meaning that the Android lifecycle calls will start here.
OnCreate
Uses setContentView so you can instantiate your starting class objects and then reference its
member’s right after it returns to have access to UI objects
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// get reference to our surface
drawingSurface = (DrawingSurface)
findViewById(R.id.drawing_surface);
drawingSurface.setTextView((TextView)
findViewById(R.id.pause_message));
}
OnCreateOptionsMenu
Here your options menu from an XML layout file is created. Also we can create the handler for menu
option selections.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
OnOptionsItemSelected
Here you start the handling of the menu item selections. Using the item id marked in the menu
xml file we can tell which menu button was pressed and execute code as we need to.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.restart:
drawingSurface.menuRestart();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
OnResume
On resume the application should have the view objects ready and can start animating the intro
for a splash screen effect. We implement the Animation Listener so we can tell when starting and
finishing animating the view for the user.
@Override
protected void onResume() {
super.onResume();
// start the application out by modifying the view and don't
accept user
// input until the animation ends
Animation anim = AnimationUtils.loadAnimation(this,
R.anim.intro);
anim.reset();
anim.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// if the animation isn't finished we shouldn't be
drawing on it
drawingSurface.introFinished = false;
}
@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation animation) {
// this animation is longest and touch should be
ready when this
// completes
drawingSurface.introFinished = true;
}
});
Animation anim2 = AnimationUtils.loadAnimation(this,
R.anim.warp_in);
anim2.reset();
drawingSurface.clearAnimation();
drawingSurface.startAnimation(anim);
drawingSurface.messageTextView.clearAnimation();
drawingSurface.messageTextView.startAnimation(anim2);
drawingSurface.onResume();
}
Drawing Surface
IncomingHandlerCallback
Allows the application to show messages to the user for different states by setting text to a
TextView object and showing or hiding this on the UI. The variables for the code to run are packed into
the Message object using a Bundle object of values.
class IncomingHandlerCallback implements Handler.Callback {
/**
* The Message object can contain more than one value.
*/
@Override
public boolean handleMessage(Message m) {
// handle message code
messageTextView.setVisibility(m.getData().getInt("show"));
messageTextView.setText(m.getData().getString("message"));
return true;
}
}
Constructor
In the constructor we register the surfaceholder callback’s and allow the application to perform
actions related to creating and drawing on your drawing surface. We create a new Handler object to
send messages or run code to update the UI. Finally, we create a new TargetThread, the state of this on
resuming the application will determine whether to restart or start an entirely new thread.
public DrawingSurface(Context con, AttributeSet attrs) {
super(con, attrs);
context = con;
// register the call back interface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// prepare the thread and its message handler (handlers can also
execute
// code if needed)
myHandler = new Handler(new IncomingHandlerCallback());
targetThread = new TargetThread(getHolder(), con, myHandler,
this);
}
SurfaceCreated
Surface created is called on start of the application and will start your thread if it’s in the NEW
state. If restarting the application the thread may be TERMINATED, in which case a new one is created
and started.
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (targetThread.getState() == Thread.State.TERMINATED) {
targetThread = new TargetThread(holder, context, myHandler,
this);
targetThread.start();
targetThread.setRunning(true);
} else if (targetThread.getState() == Thread.State.NEW) {
targetThread.start();
targetThread.setRunning(true);
}
}
SurfaceChanged
Logic is handled in the target class, there the storing of and usage of the screen size is
performed. At this point the values reflect the screen size minus the status bar or UI objects using screen
space, what is left is the actual pixels of the full screen that we can manipulate for our application.
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
targeting.surfaceChanged(holder, height, width);
}
SurfaceDestroyed
Execution of the running thread should be blocked when destroying the surface.
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
targetThread.setRunning(false);
while (retry) {
try {
targetThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
Draw
For this project, the targeting class will handle our draw methods. We use the screen height and
width and draws things relative to the screen size to ensure a similar experience on all devices.
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
targeting.draw(canvas);
}
UpdatePhysics
The targeting class handles the logic in updating physics. Depending on the state we may be in
the running or the end state and the physics will change.
public void updatePhysics() {
targeting.updatePhysics();
}
OnTouchEvent
Call performClick() if ACTION_UP this will allow for the accessibility methods to fire, which are
needed for users that have special needs. Check for intro animation to have finished to accept user
input. Unpause thread and begin cycling the draw/update loop. On the next touch event the code
should then start performing the logic of the targeting classes’ implementation. There is also the chance
that the application is restarting and the thread needs to be created immediately on touch.
@Override
public boolean performClick() {
super.performClick();
return true;
}
public boolean onTouchEvent(MotionEvent event) {
synchronized (targetThread.mSurfaceHolder) {
// if restarting the thread may not be valid, surface created will
// not be called to do this for us.
if (recreateThread) {
recreateThread = false;
if (targetThread.getState() == Thread.State.TERMINATED) {
targetThread = new TargetThread(getHolder(), context,
myHandler, this);
targetThread.start();
targetThread.setRunning(true);
} else if (targetThread.getState() == Thread.State.NEW) {
targetThread.start();
targetThread.setRunning(true);
}
}
if (event.getAction() == MotionEvent.ACTION_UP) {
performClick();
}
/**
* If the intro is finished we can perform actions on the touch
* events.
*/
if (introFinished && touchReady)
switch (targetThread.mMode) {
case TargetThread.STATE_PAUSE:
targetThread.setState(TargetThread.STATE_RUNNING);
break;
case TargetThread.STATE_RUNNING:
return targeting.onTouch(event);
}
return super.onTouchEvent(event);
}
}
SetTextView
Sets a TextView object reference that will display messages to the user. Depending on state it
will be hidden or shown to the user.
public void setTextView(TextView textView) {
messageTextView = textView;
}
OnWindowFocusChanged
When the SurfaceView View is in view and can accept user touch we can set a flag to let the
touch listener know we are ready or done accepting user touch in this View.
public void menuRestart() {
targeting.menuRestart();
}
MenuRestart
Handle’s the user pressing menu options to restart the game, this method is called after the
method declared to handle the menu selection in MainActivity is called. To easily find this out move the
cursor over the method call menuRestart() and press Control-Shift-G to see where in the code the
method is being called from. The opposite is pressing F3 on a method call to go straight to the method
code.
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus) {
touchReady = true;
} else {
touchReady = false;
}
}
OnResume
The targetThread should be newly created and ready to start but if the application is restarting
this won’t be the case. Create a flag so when the user touches the screen next the thread will begin.
public void onResume() {
if (targetThread.getState() == Thread.State.TERMINATED) {
recreateThread = true;
}
}
OnPause
If the target thread is running it should be paused and set the program execution path to be
false so it won’t run the thread.
public void onPause() {
if (targetThread != null) {
targetThread.pause();
targetThread.setRunning(false);
}
}
Target Thread
The Thread class is usually alive for shorter amounts of time than the Activity itself. If you use DDMS
perspective in Eclipse you can see the Activities and Threads alive from other applications and their
consumption of resources and the impact of your new activity on top of it.
mRun
Used to tell if the application should be running the thread loop of update and draw.
public boolean mRun = false;
mMode
Used to tell what state the application is in, and if to send messages from the UI.
public int mMode;
Run
Here we will get a new canvas from the surface holder. If the application is running then the
update physics method will be called as well as the draw method
@Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
if (c != null) {
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
drawingSurface.updatePhysics();
drawingSurface.draw(c);
}
}
} finally {
// do this in a finally so that if an exception is
thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
SetState
Called on changing of the state of the targetThread. If you are changing the state to be running
then you will call the handler and set the TextView object that is displaying the text to be hidden from
the user. If setting the state of the thread to be paused then you can send a message via the Handler
and make the TextView visible so the user knows the thread is paused. The Message object contains a
set of data that is packed into the Bundle object.
public void setState(int mode, CharSequence message) {
synchronized (mSurfaceHolder) {
mMode = mode;
Message msg;
Bundle bundle;
switch (mMode) {
case STATE_RUNNING:
msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putString("message", "");
b.putInt("show", View.INVISIBLE);
msg.setData(b);
mHandler.sendMessage(msg);
break;
case STATE_PAUSE:
Resources res = context.getResources();
CharSequence str = "";
str = res.getText(R.string.message_text);
if (message != null) {
str = message + "\n" + str;
}
msg = mHandler.obtainMessage();
bundle = new Bundle();
bundle.putString("message", str.toString());
bundle.putInt("show", View.VISIBLE);
msg.setData(bundle);
mHandler.sendMessage(msg);
break;
}
}
}
Targeting
Targeting Class
The targeting class contains the inner class Target, this contains a Rect object that will provide the
bounds of the objects on screen and determine if they are interacting each other or should be from a
physics standpoint. Two objects use this class the homing object and the target object.
To handle the drawing and physics updating I created Factory classes to return the specific classes
that I am using at that application state’s time. For this sample I have two methods, the draw() method
and the updatePhysics() method which both have a “running” and an “end” implementation.
The Factory Pattern
Using the Factory pattern you can switch out different implementations for the same task. Such as if
you are playing a game with levels one level may have different speeds or settings for things such as
background color. In this case we are switching between “running” and “end” states for our application.
In creating a new Draw or UpdatePhysics class we start with using the “running” implementation but
this will change on the “end” state and we will want to draw or update our end screen with the “end”
implementation. The factories will create these objects of one type implementing a single interface.
The method calls for draw() and updatePhysics() are captured in a base interface and each class
using that interface should have some differences in the implementation. In the sample project provided
we have two draw classes. They both have the draw() method implemented but the exact
implementation is different.
Draw
The initial Draw class being used is the “running” implementation. Inside the DrawFactory a new
Draw object is drawn based on the value of currentDraw. Drawing the target object, homing object and
wallpaper background is handed to the Draw class created by the Factory.
String currentDraw = "running";
Draw draw = DrawFactory.createDraw(currentDraw);
public void draw(Canvas canvas) {
draw.draw(canvas, this);
}
public class DrawRunningImpl implements Draw {
@Override
public void draw(Canvas canvas, Targeting targeting) {
// draw based on targeting state before or after press
if (!targeting.isTouchTimingSet)
canvas.drawColor(Color.BLUE);
else if (targeting.isTouchedOnTime)
canvas.drawColor(Color.GREEN);
else
canvas.drawColor(Color.RED);
// draw on the canvas the two rectangle objects.
canvas.drawRect(targeting.targetRect.rect,
targeting.targetObjectPaint);
canvas.drawRect(targeting.homingRect.rect,
targeting.targetHomingPaint);
}
}
public class DrawEndImpl implements Draw {
/**
* the draw class must be called if its implementing Draw
*/
@Override
public void draw(Canvas canvas, Targeting targeting) {
canvas.drawColor(Color.YELLOW);
}
}
You can see the DrawRunningImpl and DrawEndImpl only differ in that the end phase simply
draws a yellow background. While running, the implementation still has decisions to make on wether
the user has touched the screen yet and if the application has updated the applications since then. But
they both use the same draw(Canvas canvas, Targeting targeting) method signature.
MyRect
An inner class that is used to represent the target rectangle and the homing rectangle.
public class MyRect {
/**
* will be used to determine the draw pixels of the object.
*/
public Rect rect;
/**
* Create a new Rect when you create a new Target.
*/
public MyRect() {
rect = new Rect();
}
}
public MyRect homingRect = new MyRect();
public MyRect targetRect = new MyRect();
SurfaceChanged
After the screen’s actual pixels are available to manipulate the method surfaceChanged is called
with the new height and width as parameters. SurfaceChanged is NOT called after onRestart().
public void surfaceChanged(SurfaceHolder holder, int height, int width) {
// called when the surface is created for the thread
screenHeight = height;
screenWidth = width;
// by scaling the startTargetPixel from the smallest side we can
make
// sure that the start target pixel is in a similar location on
// different devices and closes in on itself.
if (screenWidth <= screenHeight)
startTargetPixel = screenWidth / 20;
else
startTargetPixel = screenHeight / 20;
// place targets
initMyRects();
}
OnTouch
To register a valid touch the press has to be while the point is on the target location and is
within the buffer range. We set a flag to let the updatePhysics class know that it must handle the touch
event and change the physics accordingly. So first we flag that there is a new touch event and next we
have a flag so we make sure to check if it’s on time or a miss.
public boolean onTouch(MotionEvent event) {
// touch tracks the life cycle of the touch event
if (!isRoundOver && !isTouched) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// touch event has happened
isTouched = true;
isTouchUpdated = false;
return false;
}
}
return true;
}
UpdatePhysics
Our Draw class works in tandem with the UpdatePhysics class to refresh and update the screen
that is in front of the user. Using our Factory we can get the new draw method when we are in the
“end” state. Our TargetThread makes calls to updatePhysics only when the application is running.
Otherwise, what we want the thread to do is draw the same screen until the game should be changing.
String currentPhysics = "running";
UpdatePhysics updatePhysics = UpdatePhysicsFactory
.createUpdatePhysics(currentPhysics);
public void updatePhysics() {
updatePhysics.updatePhysics(this);
// the moving homing rectangle has minimized completely
if (updatePhysics.getIsToBeReset()) {
currentPhysics = "end";
updatePhysics = UpdatePhysicsFactory
.createUpdatePhysics(currentPhysics);
currentDraw = "end";
draw = DrawFactory.createDraw(currentDraw);
}
}
public void updatePhysics(Targeting targeting) {
// handle user touch
if (targeting.isTouched && !targeting.isTouchUpdated) {
targeting.isTouchUpdated = true;
targeting.isTouched = false;
// check for touch on time
if (targeting.homingRect.rect.left >
(targeting.targetRect.rect.left - targeting.STROKE_WIDTH)
&& (targeting.homingRect.rect.left <
targeting.targetRect.rect.left
+ targeting.STROKE_WIDTH)) {
targeting.isTouchedOnTime = true;
targeting.isTouchTimingSet = true;
// reduce the home target for next round
targeting.targetRect.rect.left +=
targeting.startTargetPixel;
targeting.targetRect.rect.top +=
targeting.startTargetPixel;
targeting.targetRect.rect.right -=
targeting.startTargetPixel;
targeting.targetRect.rect.bottom -=
targeting.startTargetPixel;
// register the current round to be over
if (targeting.targetRect.rect.left >
targeting.screenWidth / 2) {
isToBeReset = true;
}
} else {
// count as missed and end round
targeting.isTouchedOnTime = false;
targeting.isTouchTimingSet = true;
targeting.isRoundOver = true;
// expand the target to show it was off time
targeting.targetRect.rect.left -=
targeting.startTargetPixel;
targeting.targetRect.rect.top -=
targeting.startTargetPixel;
targeting.targetRect.rect.right +=
targeting.startTargetPixel;
targeting.targetRect.rect.bottom +=
targeting.startTargetPixel;
// don't let it set it back further than the first
visible step
if (targeting.targetRect.rect.left <= 0) {
targeting.targetRect.rect.left =
targeting.startTargetPixel;
targeting.targetRect.rect.top =
targeting.startTargetPixel;
targeting.targetRect.rect.right =
targeting.screenWidth - 1
- targeting.startTargetPixel;
targeting.targetRect.rect.bottom =
targeting.screenHeight
- targeting.startTargetPixel;
}
}
}
// reduce square size of homing target
targeting.homingRect.rect.left++;
targeting.homingRect.rect.top++;
targeting.homingRect.rect.right--;
targeting.homingRect.rect.bottom--;
// when the left meets the right or the top meets the bottom
means the
// round is over and user touch will be accepted
if (targeting.homingRect.rect.left >=
targeting.homingRect.rect.right
|| targeting.homingRect.rect.top >=
targeting.homingRect.rect.bottom) {
targeting.isTouched = false;
targeting.isTouchedOnTime = false;
targeting.isTouchTimingSet = false;
targeting.isTouchUpdated = false;
targeting.isRoundOver = false;
targeting.homingRect.rect.left = 0;
targeting.homingRect.rect.right = targeting.screenWidth 1;
targeting.homingRect.rect.top = 0;
targeting.homingRect.rect.bottom = targeting.screenHeight;
}
}
InitMyRects
Android uses X and Y coordinates to map the view in front of the user but the origin, the (0,0)
coordinate is not at the center of the screen in Android. It’s actually in the top left corner of the screen.
We set the homingObject to be at this point and stretch all the way to the other side of the screen, but
we have to take one pixel from the right side of the screen to make sure the right side of the target is
visible the same is true for the bottom of the screen. Using the startTargetPixel we can set the
homingTarget inside the surfaceView and take the difference away from all sides to center it and once
again subtracting one from the right side of the screen and the bottom of the screen.
private void initMyRects() {
homingRect.rect.top = 0;
homingRect.rect.left = 0;
homingRect.rect.right = screenWidth - 1;
homingRect.rect.bottom = screenHeight - 1;
targetRect.rect.top = startTargetPixel;
targetRect.rect.left = startTargetPixel;
targetRect.rect.right = screenWidth - startTargetPixel - 1;
targetRect.rect.bottom = screenHeight - startTargetPixel - 1;
}
MenuRestart
If the user has pressed the menu restart option we have to perform actions to set the
application back to its original state. This means to reset the physics to be our original physics version, in
this case “running”. Also we reset the draw and physics to be the initial “running” implementations.
public void menuRestart() {
// reset our factory methods
currentPhysics = "running";
updatePhysics = UpdatePhysicsFactory
.createUpdatePhysics(currentPhysics);
currentDraw = "running";
draw = DrawFactory.createDraw(currentDraw);
// reset our MyRect objects
initMyRects();
//reset our flags for the targeting class
isTouched = false;
isTouchedOnTime = false;
isTouchTimingSet = false;
isTouchUpdated = false;
isRoundOver = false;
}
Testing Touch
1. Create New click Other select Android Test Project
2. Name the project
3. Select touch demo project to be tested choose build or 4.4W
4. Click FinishCreate a new class in the new package
a. Name it TouchTests
b. Have it extend ActivityInstrumentationTestCase2<MainActivity>
c. Be sure to import the package of the test class.
d. I import all the classes via touch.demo.*;
e. Hold an instance of the Activity
f. Run the class file not the project as an Android JUnit test.
package touch.target.test;
import android.test.ActivityInstrumentationTestCase2;
import touch.target.MainActivity;
import touch.target.surface.TargetThread;
public class TouchTargetsTest extends
ActivityInstrumentationTestCase2<MainActivity> {
private MainActivity mainActivity;
public TouchTargetsTest() {
super(MainActivity.class);
}
public TouchTargetsTest(Class<MainActivity> activityClass) {
super(MainActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mainActivity = getActivity();
}
public void testActivityNotNull(){
assertNotNull(mainActivity);
}
public void testStartStatePaused() {
int mode = mainActivity.drawingSurface.targetThread.mMode;
assertEquals(mode, TargetThread.STATE_PAUSE);
}
}
Import Git
You can use Eclipse to import the TouchTarget Demo project if you like. It is hosted on Github
and you can import it directly from the Eclipse IDE.
1. Open Eclipse
2. Alt-F import
3. Git
a. Project from Git
b. Clone URI
i. https://github.com/rperez22/TouchDemo
ii. No password or user name is needed with public repositories.
c. From here you will need the appcompat_library for backwards compatibility.
d. You can create a new project for SDK 5.0 and this should have the library built for you,
or you can build it from the SDK by selecting Import, select Android, Existing Android
Code Into Workspace and selecting from inside the SDK the directory
/extras/android/support/v7/appcompat.
e. The Project should build out and now you can set the Touch Demo project to use the
library.
f. Go to project name in Project Explorer
g. Click Alt-Enter to
h. Select Android
i. In the bottom is the Library section
i. Remove the invalid library link
ii. Click Add and select the appcompat support library v7
j. Now you can clean and build without errors.
k. Once this is done you can plug in your device.
l. Finally select Run as Android Application.
Attribution
DISCLAIMER: The sample code described herein is provided on an "as is" basis, without warranty of any
kind, to the fullest extent permitted by law. MobileApplications009 does not warrant or guarantee the
individual success developers may have in implementing the sample code on their development
platforms or in using their own IDE, all samples were developed in Eclipse Luna 4.4.
MobileApplications009 does not warrant, guarantee or make any representations regarding the use,
results of use, accuracy, timeliness or completeness of any data or information relating to the sample
code. MobileApplications009 disclaims all warranties, express or implied, and in particular, disclaims all
warranties of merchantability, fitness for a particular purpose, and warranties related to the code, or
any service or software related thereto.
MobileApplications009 shall not be liable for any direct, indirect or consequential damages or costs of
any type arising out of any action taken by you or others related to the sample code.
Android, Google and Google Play are trademarks of Google Inc.
Portions of this pdf are reproduced from work created and shared by the Android Open Source Project and used
according to terms described in the Creative Commons 2.5 Attribution License.
Portions of this pdf are modifications based on work created and shared by the Android Open Source Project and
used according to terms described in the Creative Commons 2.5 Attribution License.
‘Eclipse’and ‘Eclipse Ready’ are trademarks of Eclipse Foundation, Inc.
Thanks very much,
Richard A. Perez
MobileApplications009
Mobileapplications009@gmail.com