Skip to content

This tutorial is for Google’s Android mobile operating system. If you haven’t already heard about Android, then check it out immediately because it’s way cool. We have benefited so much from the Android developer community that we want to give back our own insights into the platform and how to better design/develop on the platform.

For this tutorial, we’re going to help the several people that have asked us how to create transparent panels. While we show how to overlay onto a Google Map, you can use the same technique to overlay a transparent panel onto any other view.

Starting at the end, this what we’ll develop today – a transparent panel with a single button displayed at the base of an Android MapView

Tutorial 1 - final result

Tutorial 1 - final result (closeup)

We’ll assume that you already know the basics of Android programming and will only address these “advanced” topics:

1) Creating a class that can draw a transparent background and border.
2) Adding a custom View class as a declaration in your layout XML.

(1) Creating a Custom Layout as a Transparent Panel

We wanted our transparent panel to hold children so we looked for the most natural Android view to extend and selected Linear Layout because we wanted our TransparentPanel class to layout its children horizontally. We could just as easily chosen to extend RelativeLayout of any other layout class.

TransparentPanel extends LinearLayout

The ‘magic’ of TransparentPanel happens in the dispathDraw() method. For those of you that have already created their own custom Views, you might wonder why we override dispatchDraw() instead of onDraw(). For some reason, LinearLayout does not call it’s own onDraw() method…apparently because its developer assumes a LinearLayout would never have anything to draw. BUT we want our TransparentPanel to draw a background so we override dispatchDraw() to draw the background and then let super.dispatchDraw(canvas) render any child components.

protected void dispatchDraw(Canvas canvas) {

RectF drawRect = new RectF();
drawRect.set(0,0, getMeasuredWidth(), getMeasuredHeight());

canvas.drawRoundRect(drawRect, 5, 5, innerPaint);
canvas.drawRoundRect(drawRect, 5, 5, borderPaint);

super.dispatchDraw(canvas);

}

For those new to drawing their own graphics, let’s review a few items here. First, we populate a RectF with the coordinates of the background that we want to draw. Next we make to calls to drawRoundRect(). The 1st call passes innerPaint to draw the transparent gray background while the 2nd call passes the white border that we want to paint. Lastly we call super.dispatchDraw(canvas) to render our child components (in this case a Button).

The gray background is rendered with an alpha (transparency ) == 225. This allows just enough of the map to show through.

innerPaint.setARGB(225, 75, 75, 75);

And borderPaint allows us to render a white border with a Stroke of width = 2.

borderPaint = new Paint();
borderPaint.setARGB(255, 255, 255, 255);
borderPaint.setAntiAlias(true);
borderPaint.setStyle(Style.STROKE);
borderPaint.setStrokeWidth(2);

As we did above, make sure to setAntiAlias(true) so the borders of your paint blend seamlessly with its surroundings. Set this option to false to see how your borders would have jagged edges otherwise.

(2) Adding our custom TransparentPanel class as a declarations in the layout XML.

Now we’re ready to insert the TransparentPanel into our layout XML class and to add a button. To reference our new class, we simply provide the full classpath to our the TransparentPanel and then provide layout parameters as we would for any LinearLayout. In this case, we provide padding so our Button does not rest against the edges of our TransparentPanel’s border.

<com.pocketjourney.view.TransparentPanel

android:id=”@+id/transparent_panel”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:paddingLeft=”5px”
android:paddingTop=”5px”
android:paddingRight=”5px”
android:paddingBottom=”5px”>

<Button android:id=”@+id/button_click_me”

android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Click Me!”>

</com.pocketjourney.view.TransparentPanel>

And that’s it. Here is the .apk you can use along with the source files: tutorial1.zip.

Please give us your feedback and let us know any suggestions for improving this tutorial. Also please visit these other tutorials for more tips:

This is a long tutorial, but for those of you that have been struggling with streaming of media to Google’s Android’s MediaPlayer, then I hope this tutorial proves useful as you finalize your entries into Google’s Android Challenge

This tutorial will show how to roll your own streaming media utility for Android’s MediaPlayer. We will buffer 10 seconds of audio and start playing that audio while the rest of the audio loads in the background. We store the streamed audio locally so you could cache it on device for later use or simply let it be garbage collected.

Here’s the source code for those that just want to jump in. You’ll also notice code for the other tutorials as I didn’t have time to strip them out.

Here are a few screenshots of what we’ll be creating:

Tutorial #3 results screenshots

Basic Layout

The tutorial consists of just two classes:

Tutorial3: Contains the UI layout and process button clicks
StreamingMediaPlayer: Connects to the server, downloads audio into the buffer, and controls the functionality to ensure the audio continues to play seamlessly.

We’ll assume you know about UI layout using Android’s XML resource files and will instead jump right into the audio streaming code.

Start Your Streaming

Upon clicking the “Start Streaming” button, Tutorial3 creates an instance of StreamingMediaPlayer.

new StreamingMediaPlayer(textStreamed, playButton, streamButton,progressBar);

All UI elements are passed to StreamingMediaPlayer so it can perform UI update itself. In a more robust implementation, StreamingMediaPlayer would fire relevant update events and Tutorial3 would handle the UI updates. For simplicity & cleaner code in this tutorial however, StreamingMediaPlayer will be directly updating the UI.

Tutorial3 then calls StreamingMediaPlayer.startStreaming():

audioStreamer.startStreaming(”http://www.pocketjourney.com/audio.mp3?,1444, 180);

Three variables are passed to startStreaming(): a url for the media to stream (link to an .mp3 file in this tutorial), the length in kilobytes of the media file, and the lenght in seconds of the media file. These last two values will be used when updating the progress bar.

AudioStreamer.startStreaming() creates a new thread for streaming the content so we can immediately return control back to the user interface.

public void startStreaming(final String mediaUrl, long mediaLengthInKb, long mediaLengthInSeconds) throws IOException {

this.mediaLengthInKb = mediaLengthInKb;
this.mediaLengthInSeconds = mediaLengthInSeconds;

Runnable r = new Runnable() {

public void run() {

try {

downloadAudioIncrement(mediaUrl);

} catch (IOException e) {

Log.e(getClass().getName(), “Initialization error for fileUrl=” + mediaUrl, e);
return;

}

}

};
new Thread(r).start();

}

Incremental Media Download

This is where the magic happens as we download media content from the the url stream until we have enough content buffered to start the MediaPlayer. We then let the MediaPlayer play in the background while we download the remaining audio. If the MediaPlayer reaches the end of the buffered audio, then we transfer any newly downloaded audio to the MediaPlayer and let it start playing again.

Things get a little tricky here because:

(a) The MediaPlayer seems to lock the file so we can’t simply append our content to the existing file.
(b) Pausing the MediaPlayer to load the new content takes awhile so we only want to interrupt it when absolutely necessary.
(c) Accessing the MediaPlayer from a separate thread causes it to crash.

So with those caveats in mind, here’s the method that bufferes the media content to a temporary file:

public void downloadAudioIncrement(String mediaUrl) throws IOException {

// First establish connection to the media provider
URLConnection cn = new URL(mediaUrl).openConnection();
cn.connect();
InputStream stream = cn.getInputStream();
if (stream == null) {

Log.e(getClass().getName(), “Unable to create InputStream for mediaUrl:” + mediaUrl);

}

// Create the temporary file for buffering data into
downloadingMediaFile = File.createTempFile(”downloadingMedia”, “.dat”);
FileOutputStream out = new FileOutputStream(downloadingMediaFile);

// Start reading data from the URL stream
byte buf[] = new byte[16384];
int totalBytesRead = 0, incrementalBytesRead = 0;
do {

int numread = stream.read(buf);
if (numread <= 0) {

// Nothing left to read so quit
break;

} else {

out.write(buf, 0, numread);
totalBytesRead += numread;
incrementalBytesRead += numread;
totalKbRead = totalBytesRead/1000;

// Test whether we need to transfer buffered data to the MediaPlayer
testMediaBuffer();

// Update the status for ProgressBar and TextFields
fireDataLoadUpdate();

}

} while (true);

// Lastly transfer fully loaded audio to the MediaPlayer and close the InputStream
fireDataFullyLoaded();
stream.close();

}

What’s up with testMediaBuffer()?

So if you were paying attention, an important piece of functionality must reside in the testMediaBuffer() method. You’re right. That’s the method where we determine whether we need to transfer buffered data to the MediaPlayer because we have enough to start the MediaPlayer or because the MediaPlayer has already played out its previous buffer content.

Before we jump into that, please take note that interacting with a MediaPlayer on non-main UI thread can cause crashes so we always ensure we are interacting with the UI on the main-UI Thread by using a Handler when necessary. For example, we must do so in the following method because it is being called by the media streaming Thread.

private void testMediaBuffer() {

// We’ll place our following code into a Runnable so the Handler can call it for running
// on the main UI thread

Runnable updater = new Runnable() {

public void run() {

if (mediaPlayer == null) {

// The MediaPlayer has not yet been created so see if we have
// the minimum buffered data yet.
// For our purposes, we take the minimum buffered requirement to be:
// INTIAL_KB_BUFFER = 96*10/8;//assume 96kbps*10secs/8bits per byte

if ( totalKbRead >= INTIAL_KB_BUFFER) {

try {

// We have enough buffered content so start the MediaPlayer
startMediaPlayer(bufferedFile);

} catch (Exception e) {

Log.e(getClass().getName(), “Error copying buffered conent.”, e);

}

}

} else if ( mediaPlayer.getDuration() – mediaPlayer.getCurrentPosition() <= 1000 ){

// The MediaPlayer has been started and has reached the end of its buffered
// content. We test for < 1second of data (i.e. 1000ms) because the media
// player will often stop when there are still a few milliseconds of data left to play

transferBufferToMediaPlayer();

}

}

};
handler.post(updater);

}

Starting the MediaPlayer with Initial Content Buffer

Starting the MediaPlayer is very straightforward now. We simply copy all the currently buffered content
into a new Ffile and start the MediaPlayer with it.

private void startMediaPlayer(File bufferedFile) {

try {

File bufferedFile = File.createTempFile(”playingMedia”, “.dat”);
FileUtils.copyFile(downloadingMediaFile,bufferedFile);
} catch (IOException e) {

mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(bufferedFile.getAbsolutePath());
mediaPlayer.prepare();
fireDataPreloadComplete();

Log.e(getClass().getName(), “Error initializing the MediaPlaer.”, e);
return;

}

}

Transferring Buffered Content to a MediaPlayer That is Already Playing

This is a little trickier but not much. We simply pause the MediaPlayer if it was playing (i.e. the user had not pressed pause), copy over the currently downloaded media content (which may be all of it by now) and then restart the MediaPlayer if it was previously running or had hit the end of its buffer due to a slow network.

private void transferBufferToMediaPlayer() {

try {

// Determine if we need to restart the player after transferring data (e.g. perhaps the user
// pressed pause) & also store the current audio position so we can reset it later.

boolean wasPlaying = mediaPlayer.isPlaying();
int curPosition = mediaPlayer.getCurrentPosition();
mediaPlayer.pause();

// Copy the current buffer file as we can’t download content into the same file that
// the MediaPlayer is reading from.

File bufferedFile = File.createTempFile(”playingMedia”, “.dat”);
FileUtils.copyFile(downloadingMediaFile,bufferedFile);

// Create a new MediaPlayer. We’ve tried reusing them but that seems to result in
// more system crashes than simply creating new ones.

mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(bufferedFile.getAbsolutePath());
mediaPlayer.prepare();
mediaPlayer.seekTo(curPosition);

// Restart if at end of prior beuffered content or mediaPlayer was previously playing.
// NOTE: We test for < 1second of data because the media player can stop when there is still
// a few milliseconds of data left to play

boolean atEndOfFile = mediaPlayer.getDuration() – mediaPlayer.getCurrentPosition() <= 1000;
if (wasPlaying || atEndOfFile){

mediaPlayer.start();

}

}catch (Exception e) {

Log.e(getClass().getName(), “Error updating to newly loaded content.”, e);

}

}

Conclusion

To get the real feel for how your audio will download, make sure to set it to a slower network speed. I recommend setting to AT&T’s EDGE network setting as it should give a lower limit on expected performance. You can make these setting’s easy in Eclipse by setting going into your Run or Debug setting’s dialog and making these selections.

EDGE settings in Eclipse

Well that’s it. I’ve inluded additional code for handling the ProgressBar and TextField updates but that should all be sufficiently easy to understand once you understand the rest of the code. Good luck during the next week as you finish your Android Challenge submissions.

And of course, here’s the source code. Please post a comment below if I need to explain anything in more detail. You’ll also notice code for the other tutorials as I didn’t have time to strip them out.

Reto Meier at Google Blogoscoped has written an Android tutorial on his experiences creating an application for Android which merges the maps, location api (GPS), contact book and phone dialer together. His application, Where are my Friends (WamF), leverages the core GUI and location API to figure out and map where your friends live in relation to where you are at the moment. This is an application idea suggested in Mike’s Androidology videos on Youtube, and it has quickly been achieved here in practice.

Where Are My Friends

Rob also gives some basic insight into the development environment, his chosen setup of Eclipse in Windows, and a general overview of Android foundations, so that any new Android developer can get a basic understanding of how to use Intents in Android to request and deliver information to each of the Android components that are required to perform your tasks.

Tutorial Link: Where Are My Friends

kriggio at Bumble Machine has written a helpful description of his experiences writing his first Android application (Android Blackjack). He outlines the ease at which he was able to transition his Eclipse and Java skills to the Android SDK, and discusses challenges he faced with some of the differences in accessing resources (card images). He’s also been kind enough to release his source code.

There has been a steady release of source code and tutorials this week as developers begin to port their existing applications, and write new programs for the Android platform. It’s becoming obvious that Google is really winning over the open source community with the Android platform, and it will be very exciting once the Android phones are released to have such a wealth of applications (often open source) available to us to make our phones enjoyable.

Android applications are starting to remind me of widgets. Since applications are able to mash together Android features with data from the internet, I think the possibilities really will become endless here. I’d especially like to see applications become very easy to add and remove like widgets on iGoogle and NetVibes are. The unfortunate thing about a platform like the Palm Treo 650 that I own is that applications are terrible to add and remove requiring a frustrating syncing process, plus they install databases and leave residuals behind that clog up internal memory and eventually make the system unstable. I don’t see this as being a problem with the Android platform.

Source: My first Program using the Android SDK [blog.bumblemachine.com]

Davanum Srinivas has been having some fun creating Android tutorials and releasing his sample code since the platform was released last week. Yesterday, he released his sample concept code for a Twitter client.

TwitterClient

Over the weekend, he released his BrowseMap 2.0 sample code for using Overlay to draw Starbucks locations onto a map, as an update to his earlier BrowseMap code which finds the nearest Pizza restaurant. He has also released source code for the result of the Hello Android tutorial, and written a tutorial on sending XMPP messages.

BrowseMap 2.0

Source: Show Me The Code! [davanum.wordpress.com]