Smartphones

Bouncing a ball on Android's canvas

Enterprise app developer William J. Francis has fun with Android's animation classes in his bouncing ball tutorial.

Regular readers of my App Builder posts know how much I enjoy taking a break from my day gig as an Android enterprise app developer and playing around with the multimedia capabilities of Google's operating system. I particularly enjoy Android's animation capabilities, and getting to draw directly to the canvas object.

With the release of Android 4.1 (Jelly Bean), Google did a lot of work under the hood to improve the underlying framework that drives the animation, not just in terms of speed but also in the overall smoothness and consistency of visual elements. Getting the opportunity to see the updated animation engine firsthand on my Nexus S proved a perfect excuse for me to write a bouncing ball tutorial.

While I wrote this tutorial with Android 4.1 in mind, there is nothing that is 4.1 specific. Running it on an older device simply means you won't get to see the souped up Android animation engine in action. You can follow along with the step-by-step outline below, or download and import the entire project.

1. Create a new Android project in Eclipse. Target Android 1.6 or higher. Don't forget to rename the startup activity to Main.java, along with the associated layout resource.

2. Because we will be drawing the entire canvas ourselves, we will want to handle orientation ourselves as well. This is done by modifying the activity in the AndroidManifest.xml file.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.authorwjf.bounce"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="4"
        android:targetSdkVersion="15" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:screenOrientation="portrait" android:configChanges="orientation|keyboardHidden"
            android:name=".Main"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
          </activity>
    </application>
</manifest>
3. The application uses an image to represent our ball. You can use any image you like, and it doesn't have to be round. I chose the image of a tennis ball (Figure A), which I then saved into the /res/drawable folder. Figure A

4. Next we want to define our /res/layout/main.xml file. You will see I've only defined a single custom view; this is a class we will be creating shortly.

main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="#000000">
    <com.authorwjf.bounce.AnimatedView
                android:id="@+id/anim_view"
                android:layout_height="fill_parent"
                android:layout_width="fill_parent"/>
</LinearLayout>

5. It turns out we can use the auto generated Main.java file exactly as is.

Main.java

package com.authorwjf.bounce;

import android.os.Bundle;
import android.app.Activity;

public class Main extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
   }
}

6. What remains is to create our custom view class, AnimatedView.java. The class consists of a constructor, a runnable, and the on draw override. There is no tricky math or physics involved for our bouncing ball -- I'm simply detecting when it hits an edge and reversing the direction.

AnimatedView.java

package com.authorwjf.bounce;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ImageView;
public class AnimatedView extends ImageView{
        private Context mContext;
        int x = -1;
        int y = -1;
        private int xVelocity = 10;
        private int yVelocity = 5;
        private Handler h;
        private final int FRAME_RATE = 30;

public AnimatedView(Context context, AttributeSet attrs)  {
                super(context, attrs);
                mContext = context;
                h = new Handler();
      }
         private Runnable r = new Runnable() {
                 @Override
                 public void run() {
                         invalidate();
                 }
         };
         protected void onDraw(Canvas c) {
                 BitmapDrawable ball = (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.ball);
             if (x<0 && y <0) {
                 x = this.getWidth()/2;
                 y = this.getHeight()/2;
             } else {
                 x += xVelocity;
                 y += yVelocity;
                 if ((x > this.getWidth() - ball.getBitmap().getWidth()) || (x < 0)) {
                         xVelocity = xVelocity*-1;
                 }
                 if ((y > this.getHeight() - ball.getBitmap().getHeight()) || (y < 0)) {
                         yVelocity = yVelocity*-1;
                 }
            }
            c.drawBitmap(ball.getBitmap(), x, y, null);
            h.postDelayed(r, FRAME_RATE);
      }
}
The screen shots are static, so in order to enjoy the bounce effect you will need to load the application to a device (Figure B). It will run on the emulator, but the animations are more impressive on an actual device -- that goes double if you are lucky enough to have a device running Jelly Bean. Figure B

With each iteration of Android things just keep getting better. I can't wait to see what Google has in store for us next.

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

2 comments
smartbean
smartbean

This is a very useful example. I'd like to start an animation like this, then launch an intent when the user clicks on the screen. However, I can't launch an intent because the activity context isn't available; I tried various workarounds but didn't find a way to make this work. I also tried adding a method to Main to launch the intent, but I can't invoke it from the animation class because it wants to invoke it as a static method, but launching the intent requires non-static methods. I don't know why Android makes such simple tasks so difficult, but would appreciate a suggestion of how to do this.

KingDestruct
KingDestruct

Hello William, I am a programming student in California and I am currently taking an Android programming class. For one of our assignments we are using a modified version of the code you have just presented, but I'm running into a small error. In your main.xml where it says "