Using animations in your Android applications can help create an interesting and engaging experience for your users. They can be used in games and applications in a number of ways either as a visual treat or as a method to convey information. Fortunately Android has an animation framework that allows you to easily translate, rotate, scale and alter the alpha of (or a combination) a View. Common transitions like fade in/out or slide in from off screen are examples of animations the framework can produce.

Animations and Interpolators

The process of animating a View makes use of two classes the Animation and Interpolator classes. The Animation class describes various aspects of the animation such as the transformation (changes in position, alpha, etc) and the duration of the animation.

The Interpolator class describes the rate of change of the animation, for example a ScaleAnimation with an AccelerateIterpolator results in a View that scales slowly at first and speeds up until the end of the animation. There are a number of different interpolators the most basic LinearInterpolator to more complex ones such as AccelerateInterpolator, AccelerateDecelerateInterpolter and OvershootInterpolator.

Adding Animation to a View

Here is res/layout/main.xml, it contains a button called ‘butt’ that is centred in the screen.

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

	<Button android:id="@+id/butt" android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_centerInParent="true" android:text="Shake!"/>

</RelativeLayout>

Now we define our animation, res/anim/exampe_anim.xml , which is going to slide the button in from the bottom of the screen to the centre of the screen scaling from 0.5 to 1 as it travels all in 1.5 seconds. The p in 50%p means parent, since the Button is a primary child of the RelativeLayout this means 50% of the screen height.

<?xml version="1.0" encoding="utf-8"?>

	<set xmlns:android="http://schemas.android.com/apk/res/android"
	android:interpolator="@android:anim/decelerate_interpolator" android:shareInterpolator="true">

		<translate android:fromYDelta="50%p" android:toYDelta="0" android:duration="1500" />

		<scale android:fromYScale="0.5" android:toYScale="1" android:fromXScale="0.5"
		android:toXScale="1" android:duration="1500" />

</set>

To start the animation use AnimationUtils.loadAnimation(context, resID) to load the animation and the startAnimation(animation) method of the View class.

butt.startAnimation(AnimationUtils.loadAnimation(getBaseContext(), R.anim.example_anim));

Read more: Creating Custom Animations

Using a .ttf or .otf font in Android is fairly straightforward. Firstly create ‘assets/fonts/’ and put the files in there. Now using Typeface.createFromAsset we can create the typeface.

Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/CustomFont.ttf");

To set TextView to use this typeface we use the setTypeface function.

TextView tv = (TextView) findViewById(R.id.texty);
tv.setTypeface(tf);

You can, of course, use this in your custom views with Paint.setTypeface.

Words of Warning

In general I wouldn’t recommend using fonts other than Droid font family apart from in games. That is because Ascender Corp specifically developed the Droid font family for optimal quality and readability on small screen devices. In an application the font is typically a form in which to present some information rather than containing information itself. For example in a racing you might use a font that is elongated with spoilers resembling a car but you probably would not use such a font for an article about cars.

Limitations

As far as I’m aware it is not possible to reference a font in XML. Making something like this impossible:

<TextView
android:id = ”@+id/texty”
android:typeface = "@font/CustomFont"
/>

I think that the Android team may have purposefully excluded a mechanism for referencing custom typefaces in XML to deter people from using an insane custom font that is difficult to read and potentially quite large.

On Android, and in general, you want your application or game to be responsive. So operations that take a long time to finish or block I/O should not be preformed on the UI thread otherwise the UI becomes clunky and unresponsive. To solve this problem any CPU intensive or I/O blocking operation are preformed in a separate thread. Unfortunately this creates another problem, which is how do we update and control the UI from this separate worker thread.

This is where Handlers come into play they allow you to send Messages (information and instructions) from the worker thread to the UI thread. A Message can contain ints using the fields arg1 and arg2 and an arbitrary Object using the obj or a Bundle using the setData(Bundle data) method.

So here’s an example of how to use Handlers:

public class DataView extends View {
	final static int FAILURE = 123, DATA = 124;
	DataHandler handler;
	ArrayList data;
	boolean failure = false;

	public DataView(Context context, AttributeSet attrs) {
		super(context, attrs);
		handler = new DataHandler();
		(new Thread(new WorkerRunnable(this))).start();
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// draw something based on data…
	}	

	private class DataHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case FAILURE:
				failure = true;
				invalidate();
				break;
			case DATA:
				data = (ArrayList) msg.obj;
				invalidate();
				break;
			default:
				super.handleMessage(msg);
				break;
			}
		}
	}

	@Override
	public Handler getHandler() {
		return handler;
	}

}
public class WorkerRunnable implements Runnable {

	private DataView dataView;
	private ArrayList dataObjects = new ArrayList(10);

	public WorkerRunnable(DataView dataView) {
		this.dataView = dataView;
	}

	@Override
	public void run() {
		boolean success = true;

		// some io or cpu heavy operations

		Message msg = Message.obtain();
		if(success){
			msg.what = DataView.DATA;
			msg.obj = dataObjects;
		}else{
			msg.what = DataView.FAILURE;
		}
		dataView.getHandler().sendMessage(msg);
	}
}

Explanation

The DataView spawns a Thread that runs a WorkRunnables code, which performs some IO or CPU heavy operations. The WorkRunnable informs the UI once it has finished (or failed) by sending messages to the custom written Handler.

Simon!

June 14th, 2010

I have released my first Android game on to the Market. It is basically a copy of the classic electronic memory game Simon, a game in which you repeat the gradually lengthening computer generated sequence for as many rounds as you can. The game has multiple difficulty settings, musical notes and a colourful design.

screen-capture-1screen-capture-2
The release of this game is more of an experiment to investigate how the Android Market works and game development on the platform works. Therefore the game itself is fairly unoriginal and took a short amount of time to produce but the graphics and game mechanics are of a high standard. The process has taught me some useful things about Android, for example how to use custom fonts and ‘Handlers’ to update the UI from a Thread other than the UI Thread. Both of which I shall write articles about.

Now on to my next Android project using all that I have learnt from this one.

qrcode-simon

Ideal Android Tablet

May 8th, 2010

The recent release of the iPad and announcement of the JooJoo a Linux based tablet, has inspired some want for a tablet device. But neither have great appeal; the iPad because of Apple’s megalomaniacal desire to fully control users mobile devices and the JooJoo’s clunky software not to mention the device itself is simply is too large.

Here is a pointless mock up of a device looking quite a lot like an iPad.

ideal-android-tablet

Dimensions and Display

The device would be about 7.5’’x11’’ and have a screen resolution of at least 720×1200 so that horizontally it can play 720p video without scaling. Also viewing the web or an e-book would not be strenuous or involve constant zooming and scrolling.

Operating System

Android would be the underlying operating system giving assess to a vast number of apps as well as high-quality ‘utility’ applications such as the browser and e-mail client.

Processor

The device would be powered by a 1GHz Snapdragon processor or similar possibly the upcoming 8X72 chipset. The 8X72 is capable of decoding 1080p video.

Output

Outputs include an HDMI port and a 3.5mm stereo jack. The combination of wi-fi and HDMI would allow streaming of high definition video to a HD TV. Acting like a network enabled set top box.

Maybe some day the perfect tablet with arrive but until that day I guess I’ll have to stick to reading an actual newspaper while munching on my cornflakes.

Android GPS Example

April 11th, 2010

For some reason there doesn’t seem to be a clear example showing how to use GPS on Android. Here is some code I have written that listens for location changes and displays the data in a TextView. The example also shows how to open the GPS setting with code via an Intent, since sometimes the GPS will be turned off by the user.

Code

package uk.co.hejp.GPSExample;

import android.app.Activity;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

public class Main extends Activity implements LocationListener {

	/* this class implements LocationListener, which listens for both
	 * changes in the location of the device and changes in the status
	 * of the GPS system.
	 * */

	static final String tag = "Main"; // for Log

	TextView txtInfo;
	LocationManager lm;
	StringBuilder sb;
	int noOfFixes = 0;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		/* get TextView to display the GPS data */
		txtInfo = (TextView) findViewById(R.id.textInfo);

		/* the location manager is the most vital part it allows access
		 * to location and GPS status services */
		lm = (LocationManager) getSystemService(LOCATION_SERVICE);
	}

	@Override
	protected void onResume() {
		/*
		 * onResume is is always called after onStart, even if the app hasn't been
		 * paused
		 *
		 * add location listener and request updates every 1000ms or 10m
		 */
		lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 10f, this);
		super.onResume();
	}

	@Override
	protected void onPause() {
		/* GPS, as it turns out, consumes battery like crazy */
		lm.removeUpdates(this);
		super.onResume();
	}

	@Override
	public void onLocationChanged(Location location) {
		Log.v(tag, "Location Changed");

		sb = new StringBuilder(512);

		noOfFixes++;

		/* display some of the data in the TextView */

		sb.append("No. of Fixes: ");
		sb.append(noOfFixes);
		sb.append('\n');
		sb.append('\n');

		sb.append("Londitude: ");
		sb.append(location.getLongitude());
		sb.append('\n');

		sb.append("Latitude: ");
		sb.append(location.getLatitude());
		sb.append('\n');

		sb.append("Altitiude: ");
		sb.append(location.getAltitude());
		sb.append('\n');

		sb.append("Accuracy: ");
		sb.append(location.getAccuracy());
		sb.append('\n');

		sb.append("Timestamp: ");
		sb.append(location.getTime());
		sb.append('\n');

		txtInfo.setText(sb.toString());
	}

	@Override
	public void onProviderDisabled(String provider) {
		/* this is called if/when the GPS is disabled in settings */
		Log.v(tag, "Disabled");

		/* bring up the GPS settings */
		Intent intent = new Intent(
				android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
		startActivity(intent);
	}

	@Override
	public void onProviderEnabled(String provider) {
		Log.v(tag, "Enabled");
		Toast.makeText(this, "GPS Enabled", Toast.LENGTH_SHORT).show();

	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		/* This is called when the GPS status alters */
		switch (status) {
		case LocationProvider.OUT_OF_SERVICE:
			Log.v(tag, "Status Changed: Out of Service");
			Toast.makeText(this, "Status Changed: Out of Service",
					Toast.LENGTH_SHORT).show();
			break;
		case LocationProvider.TEMPORARILY_UNAVAILABLE:
			Log.v(tag, "Status Changed: Temporarily Unavailable");
			Toast.makeText(this, "Status Changed: Temporarily Unavailable",
					Toast.LENGTH_SHORT).show();
			break;
		case LocationProvider.AVAILABLE:
			Log.v(tag, "Status Changed: Available");
			Toast.makeText(this, "Status Changed: Available",
					Toast.LENGTH_SHORT).show();
			break;
		}
	}

	@Override
	protected void onStop() {
		/* may as well just finish since saving the state is not important for this toy app */
		finish();
		super.onStop();
	}
}

Permissions

Of course the Android framework has security permissions to protect the user. To get the permission to use GPS add a uses-permission tag with attribute android:name set to “android.permission.ACCESS_FINE_LOCATION” to the AndroidManifest.xml document as a direct child of the manifest tag, i.e:

	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Notes

When testing this code in the emulator, you can send mock fixes by connecting to the emulator using telnet, i.e. ‘telnet localhost 5554′ and typing:

geo fix -10.223, 40.549

In this example data from the accelerometer is written to a TextView (Android TextArea), this example could easily be altered to use anyone of the other sensors (temperature, light, pressure, etc).

package hejp.AccelerometerExample;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import android.hardware.SensorManager;
import android.hardware.SensorEventListener;
import android.hardware.SensorEvent;
import android.hardware.Sensor;
import java.util.List;

public class AccelerometerExample extends Activity {
	SensorManager sm = null;
	TextView accText = null;
	List list;

	/* This responds to sensor events */
	SensorEventListener sel = new SensorEventListener(){
		public void onAccuracyChanged(Sensor sensor, int accuracy) {
			/* Isn't required for this example */

		}
		public void onSensorChanged(SensorEvent event) {
			/* Write the accelerometer values to the TextView */
			float[] values = event.values;
			accText.setText("x: "+values[0]+"\ny: "+values[1]+"\nz: "+values[2]);
		}

	};

    /* Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        /* Get a SensorManager instance */
        sm = (SensorManager)getSystemService(SENSOR_SERVICE);

        /* This corresponds to a TextView element in main.xml with android:id="@+id/accText" */
        accText = (TextView)findViewById(R.id.accText);

        /* Get list of accelerometers */
        list = sm.getSensorList(Sensor.TYPE_ACCELEROMETER);

        /* If there are any accelerometers register a listener to the first else
           print a little error message */
        if(list.size()>0){
        	sm.registerListener(sel, list.get(0), SensorManager.SENSOR_DELAY_NORMAL);
        }else{
        	Toast.makeText(getBaseContext(), "Error: No Accelerometer.", Toast.LENGTH_LONG);
        }
    }

	@Override
	protected void onStop() {
		/* Always a good idea to unregister, disconnect, close, etc things */
		if(list.size()>0){
			/* This actually unregisters a listener for all sensors, but it can be done
			   on a sensor by sensor basis */
			sm.unregisterListener(sel);
		}
		super.onStop();
	}

}

Personally I think that Android is the most interesting and significant mobile platform ever. The fact that it’s open and it’s level of functionality, not to mention it’s backed by Google, make it like nothing else.

Having had written mobile applications (in Java) before and enduring the seemingly endless interoperability problems and senseless security restrictions (for example bluetooth PSM restrictions making to impossible to connect to a wiimote) makes this a real excitement.