Click here to Skip to main content
Click here to Skip to main content

Tilt Ball Walkthrough

, 22 Jan 2012
Rate this:
Please Sign up or sign in to vote.
Create a simple ball app for the Android OS using the accelerometer

Introduction

This tutorial guides you through creating a simple application for the Android phone. The application demonstrates using the accelerometer to make a ball move across the screen when the phone is tilted. The project is built using Eclipse and the Android SDK.

Background

This tutorial assumes you already have the Eclipse environment up and running. If you are new to Eclipse and Android development, I recommend going through the temperature converter tutorial which can be found here.

Using the Code

You can create the project by going through the steps listed below. If you prefer to load the entire project, download\unzip the project file, then open Eclipse and choose File->Import..->General->Existing Projects and choose the root folder of the TiltBall project.

Let's begin:

Start Eclipse (I'm using Eclipse Classic version 3.6.2).

Choose File -> New -> Project -> Android -> Android Project.

Click Next.

Fill in the fields as shown below. You can use any version of Android 1.5 or later.

Click Finish.

Once the project is created, open AndroidManifest.xml.

Click on the last tab in the source editor to view the actual XML.

If you are using Android 1.6 or later, update the sdk version to 4. At version 3, phone and storage permissions will be required to install this application. Android 1.5 requires sdk version 3 (the application won't start otherwise). This value could also have been set in the "New Android Project" dialog but either way works.

<uses-sdk android:minSdkVersion="4" />

Insert the following tag to keep the phone awake while your app is running:

   .......
    </application>
    <uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>

Update the activity tag to set the application to portrait:

<activity android:name=".TiltBallActivity"
          android:label="@string/app_name"
          android:screenOrientation="portrait" 
          android:configChanges="keyboardHidden|orientation">

Open main.xml and click on the second tab to see the actual XML.

Replace the existing XML with this text. We'll use the Framelayout so the single child object (the ball) will fill the entire parent.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/main_view">
</FrameLayout>

Right-click on TiltBall->src->droid.pkg and choose New->Class.

Enter BallView as the class name. The other settings can stay as they are.

Click Finish.

In BallView.java class, replace the existing code with this code:

package droid.pkg;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;

public class BallView extends View {

    public float x;
    public float y;
    private final int r;
    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    
    //construct new ball object
    public BallView(Context context, float x, float y, int r) {
        super(context);
        //color hex is [transparency][red][green][blue]
        mPaint.setColor(0xFF00FF00);  //not transparent. color is green
        this.x = x;
        this.y = y;
        this.r = r;  //radius
    }               
        
    //called by invalidate()
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(x, y, r, mPaint);
    } 
}

In TiltBallActivity.java, delete the existing code.

Enter the package name and imports we'll need for our application:

package droid.pkg;

import java.util.Timer;
import java.util.TimerTask;
import droid.pkg.R;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.SensorEventListener;

Create the main activity and class level variables.

public class TiltBallActivity extends Activity {
    
      BallView mBallView = null;
      Handler RedrawHandler = new Handler(); //so redraw occurs in main thread
      Timer mTmr = null;
      TimerTask mTsk = null;
      int mScrWidth, mScrHeight;
      android.graphics.PointF mBallPos, mBallSpd;

Add the onCreate handler for the main activity.

@Override
public void onCreate(Bundle savedInstanceState) {

Set window properties so our app runs full screen and the phone stays awake.

requestWindowFeature(Window.FEATURE_NO_TITLE); //hide title bar
//set app to full screen and keep screen on 
getWindow().setFlags(0xFFFFFFFF,
LayoutParams.FLAG_FULLSCREEN|LayoutParams.FLAG_KEEP_SCREEN_ON);

Add the other housekeeping chores and create a pointer to the main activity.

super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//create pointer to main screen
final FrameLayout mainView = 
(android.widget.FrameLayout)findViewById(R.id.main_view);

Set the initial variable values. The ball will start center screen with a speed of zero.

//get screen dimensions
Display display = getWindowManager().getDefaultDisplay();  
mScrWidth = display.getWidth(); 
mScrHeight = display.getHeight();
mBallPos = new android.graphics.PointF();
mBallSpd = new android.graphics.PointF();
        
//create variables for ball position and speed
mBallPos.x = mScrWidth/2; 
mBallPos.y = mScrHeight/2; 
mBallSpd.x = 0;
mBallSpd.y = 0; 

Create the ball object and add it to the main screen.

//create initial ball
mBallView = new BallView(this, mBallPos.x, mBallPos.y, 5);
                
mainView.addView(mBallView); //add ball to main screen
mBallView.invalidate(); //call onDraw in BallView

Create the handler for accelerometer sensor. This will adjust the ball speed according to sensor value.

//listener for accelerometer, use anonymous class for simplicity
((SensorManager)getSystemService(Context.SENSOR_SERVICE)).registerListener(
     new SensorEventListener() {    
        @Override  
        public void onSensorChanged(SensorEvent event) {  
           //set ball speed based on phone tilt (ignore Z axis)
           mBallSpd.x = -event.values[0];
           mBallSpd.y = event.values[1];
           //timer event will redraw ball
        }
        @Override  
        public void onAccuracyChanged(Sensor sensor, int accuracy) {} //ignore
    },
    ((SensorManager)getSystemService(Context.SENSOR_SERVICE))
    .getSensorList(Sensor.TYPE_ACCELEROMETER).get(0),   
     SensorManager.SENSOR_DELAY_NORMAL);

Create the handler for the touch sensor. This will move the ball to the touch point.

    //listener for touch event 
    mainView.setOnTouchListener(new android.view.View.OnTouchListener() {
        public boolean onTouch(android.view.View v, android.view.MotionEvent e) {
            //set ball position based on screen touch
            mBallPos.x = e.getX();
            mBallPos.y = e.getY();
            //timer event will redraw ball
            return true;
        }}); 
} //OnCreate

Create the handler for user menu. There is only one menu entry (Exit) which is not really needed. The application will exit when the user switches to another application.

//listener for menu button on phone
@Override
public boolean onCreateOptionsMenu(Menu menu) 
{
    menu.add("Exit"); //only one menu item
    return super.onCreateOptionsMenu(menu);
}

Create the handler for menu selection. Only one choice for now.

//listener for menu item clicked
@Override
public boolean onOptionsItemSelected(MenuItem item) 
{
    // Handle item selection    
    if (item.getTitle() == "Exit") //user clicked Exit
        finish(); //will call onPause
    return super.onOptionsItemSelected(item);    
}

Create the handler for onPause event. This occurs when our application is moved to the background (user pressed Home\Back\etc). By default, the main activity thread will be paused but the background threads (i.e., Timer) will continue to run. To fully pause our application, we will kill the Timer thread.

//For state flow see http://developer.android.com/reference/android/app/Activity.html
@Override
public void onPause() //app moved to background, stop background threads
{
    mTmr.cancel(); //kill\release timer (our only background thread)
    mTmr = null;
    mTsk = null;
    super.onPause();
}

Create the handler for onResume. This means the application just started or the user has returned to our application before the OS removed it from memory.

@Override
public void onResume() //app moved to foreground (also occurs at app startup)
{
    //create timer to move ball to new position
    mTmr = new Timer(); 
    mTsk = new TimerTask() {
    public void run() {

You can log the Timer event to confirm when the Timer thread has ended. If you're debugging using your phone (not a virtual device), you will need to get a (free) LogCat viewer from the Android market. Log messages will not show up in the Eclipse debugger unless you use a virtual device.

//if debugging with external device, 
//  a log cat viewer will be needed on the device
android.util.Log.d("TiltBall","Timer Hit - " + mBallPos.x + ":" + mBallPos.y);

Move ball based on current ball speed (which was set in accelerometer handler).

//move ball based on current speed
mBallPos.x += mBallSpd.x;
mBallPos.y += mBallSpd.y;

If ball gets to edge of screen, set the ball position to opposite side of screen. Without this, the ball will just disappear. You could modify this code to have the ball stop at the edge instead of wrapping to the other side.

//if ball goes off screen, reposition to opposite side of screen
if (mBallPos.x > mScrWidth) mBallPos.x=0;
if (mBallPos.y > mScrHeight) mBallPos.y=0;
if (mBallPos.x < 0) mBallPos.x=mScrWidth;
if (mBallPos.y < 0) mBallPos.y=mScrHeight;

Update ball object with new position and redraw. Note that we need to redraw the ball in a background thread to prevent the main thread from locking (deadlock).

//update ball class instance
mBallView.mX = mBallPos.x;
mBallView.mY = mBallPos.y;
//redraw ball. Must run in background thread to prevent thread lock.
RedrawHandler.post(new Runnable() {
    public void run() {    
    mBallView.invalidate();
    }});
}}; // TimerTask

The end of the onResume handler. Start the timer with a 10 millisecond interval.

mTmr.schedule(mTsk,10,10); //start timer
super.onResume();
} // onResume

Add the onDestroy handler. This is called when the application is stopped (not just paused). Calling killProcess will remove the app from memory, though this is not needed as the OS will reclaim the memory when memory runs low.

@Override
public void onDestroy() //main thread stopped
{
    super.onDestroy();
    //wait for threads to exit before clearing app
    System.runFinalizersOnExit(true); 
    //remove app from memory
    android.os.Process.killProcess(android.os.Process.myPid());  
}

Add the onConfigurationChanged handler. When the user tilts the phone sideways, by default, the application will change to landscape view and call onCreate. We want to stay in portrait view, so we capture and ignore this event.

    //listener for config change. 
    //This is called when user tilts phone enough to trigger landscape view
    //we want our app to stay in portrait view, so bypass event 
    @Override 
    public void onConfigurationChanged(Configuration newConfig)
    {
       super.onConfigurationChanged(newConfig);
    }

} //TiltBallActivity

Build the project (Project->Build All). If you have Build Automatically set, the project will already be built.

Running the app:

To see the accelerometer actually work, you will need to connect your phone to the computer:

  • Connect your phone using a USB cable (you may need to install the USB drivers for your phone)
  • On your phone, in Settings->Application->Development, enable USB debugging
  • USB Storage should be disabled on your phone

In Eclipse, press F11 to start debugging.

After a few seconds (if everything goes right), the application should start on your phone.

If the Eclipse starts a virtual device instead:

  • Close the virtual device
  • Go to Run->Debug Configurations
  • In the configuration settings under Target, choose Manual. This will allow you to choose which device to debug on:

Click Debug, then select Choose a running Android device in the next screen and click OK to start debugging

If you don't see any running devices, confirm that your phone is attached to your PC and you have a working USB cable. You can also download USBDeview (freeware) which lists the attached USB devices on your PC.

To exit the app, use the menu on your phone or choose Run->Terminate in Eclipse.

To install the application to your phone using an APK file:

On your phone, in Settings->Applications, enable Unknown sources to allow non-market apps on your phone

In Eclipse, choose File->Export..->Android-> Export Android Application.

Click Next.

Enter TiltBall as the project name.

Click Next.

If you already have a keystore, choose Use existing keystore. If not, here are the steps to create one:

Choose Create new keystore. Enter a file name (no extension is needed) and a password:

Click Next.

For Alias and Password, you can use the same values you entered into the previous screen. Set validity to 100 years. Enter any name in the Name field. If you plan to publish any apps using this keystore, you should probably use your real information.

Enter the file name for your apk file.

Click Finish.

To install the apk file onto your phone, use the adb tool in the android-sdk\platform-tools folder. If you don't know the folder, just search your computer for adb.exe.

To install the apk file, use this command line:

adb install C:\TiltBall.apk

Once the install is complete, TiltBall should be available in your phones application list.

Points of Interest

A few things worth noting:

  • Writing a tutorial is harder than it looks Smile | :)
  • In my testing, onDestroy is always called whenever the app goes to the background. According to the Android state diagram, onDestroy is only called when the OS needs to free up memory. I'm unclear on the issue here.
  • To make the ball movement more realistic, the accelerometer should be used to set acceleration, not speed. This would also allow for a bounce affect when the ball hits the edge of the screen.
  • The adb tool can also be used to list Android devices connected to the PC.
  • For my testing, I'm used the Hauwei u8150 (Android 2.2) which ran well.
  • If you see any problems or see a way to improve this tutorial, drop me a note.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

mikew67
Software Developer (Senior) Credit Suisse
United States United States
Mike Waddell
 
Professional C# developer who dabbles in web and mobile development.
 
I also spend too much time playing video games and watching movies.
 
Certifications in .Net, SQL Server, and Java

Comments and Discussions

 
Questionhey mike got some question PinmemberKim Paulene Opulencia7-Apr-14 18:31 
QuestionX and Y are displaced ! Pinmemberhadi kazemi22-Feb-13 22:28 
AnswerRe: X and Y are displaced ! Pinmembermikew6726-Feb-13 5:23 
QuestionError App Pinmemberabcdawwx4-Jan-13 2:20 
AnswerRe: Error App Pinmembermikew6722-Feb-13 5:56 
Questionwhy 9*Angle? PinmemberJones Schöld28-Nov-12 22:30 
AnswerRe: why 9*Angle? Pinmembermikew6729-Nov-12 6:52 
GeneralMy vote of 5 Pinmemberpakssg30-Apr-12 4:56 
QuestionError PinmemberCurtiLong7-Apr-12 19:06 
AnswerRe: Error Pinmembermikew678-Apr-12 5:29 
GeneralRe: Error PinmemberCurtiLong8-Apr-12 18:08 
I went back to the main.xml and was missing a > sign at the end of the @id. Thanks for your fast response! Great APP!
Questionwhy do you want redraw in the main thread? PinmemberMacGinitie2-Feb-12 9:07 
AnswerRe: why do you want redraw in the main thread? Pinmembermikew678-Apr-12 5:48 
Questionaccelerometer orientation issue PinmemberMacGinitie2-Feb-12 6:07 
GeneralMy vote of 5 PinmemberMacGinitie2-Feb-12 6:03 
QuestionGreat article PinmemberAnirudhT29-Nov-11 10:27 
GeneralMy vote of 5 Pinmembergetupkid18-Oct-11 14:14 
GeneralMy vote of 5 PinmemberEd Korsberg7-Sep-11 16:40 
GeneralExcellent Article Pinmemberrakesh bothra31-Jul-11 23:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 22 Jan 2012
Article Copyright 2011 by mikew67
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid