Pages

Thursday, May 19, 2011

Hello Worlds Part 5 – Android and OpenGL

This is the last post of the series “Half Dozen Hello Worlds” where we explored different ways of communicating with Android users.
Part 1 used a simple TextView widget to display “Hello World” on the screen.
Part 2 explored using a Layout XML.
Part 3 showed you two different kinds of pop-up message.
Part 4 explored using Text to Speech to have Android actually say “Hello World” out loud.
When I was in college one of my favorite professors was Dr. Heath.  He taught Calculus and he would often spend ten or fifteen minutes in front of the board showing the steps to work out a single problem.  When he finished and the answer was 3 (or whatever) he would face the class with a big grin on his face and say, “That’s how you get 3, THE HARD WAY!”  Today we are going to display “Hello World” THE HARD WAY.
OpenGL is a very powerful Graphics Library (which is what GL stands for incidentally) which is intended to be hardware independent.  The version of OpenGL that is implemented on Android is called OpenGL ES 1.0 (The ES stands for Embedded Systems).  If you are unfamiliar with OpenGL all you really need to know right now is that it is a library for rendering 3D graphics.  We will be rendering our Hello World as a series of Line Segments in 3D space. Rendering, in case you don’t know, simply refers to displaying three dimensional objects on a two dimensional surface (i.e. your screen).
This is a more advanced topic and will, by necessity, be more involved than previous examples.  I will try my best to explain each part in detail, but please leave a comment if you have any questions as there is a lot to cover.  We are only going to scratch the surface of OpenGL on Android here, but it should be enough to get you started.
First we are going to create a new Android Project using Android 1.5 (Minimum SDK 3).  I will assume you know how to do this since it was covered in Part 1.  I named my main Activity Hello5.  Here is the source code from that activity.
package com.learnandroid.helloworld;
 
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
 
public class Hello5 extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GLSurfaceView drawSurface = new GLSurfaceView(this);
        drawSurface.setRenderer(new HelloRenderer());
        setContentView(drawSurface);
    }
}
The first thing you will probably notice here is the new import statement at the top.
import android.opengl.GLSurfaceView;
GLSurfaceView is a new kind of view that will provide us with a drawing surface on which to render our 3D objects. GLSurfaceView was introduced in Android 1.5 and is the reason we selected 1.5 for our minimum SDK for this project. GLSurfaceView will take care of providing us a place to display our graphics with very little effort on our part.
Inside on create we’ve added the following lines to the default code.
GLSurfaceView drawSurface = new GLSurfaceView(this);
drawSurface.setRenderer(new HelloRenderer());
The first line initialized the GLSurfaceView with a Context (our Activity). The second line tells our GLSurfaceView the name of the class that will perform the rendering. When you enter this code you will get an error and Eclipse will underline HelloRenderer in red. Hover over it, and select Create class ‘HelloRenderer’.
CreateHelloRendererOn the create screen that comes up you can leave all of the defaults and just click finish.  On the next page we will look at HelloRenderer.java in detail.
HelloRender.java should look like this by default.
package com.learnandroid.helloworld;
 
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
 
import android.opengl.GLSurfaceView.Renderer;
 
public class HelloRenderer implements Renderer {
 
 @Override
 public void onDrawFrame(GL10 arg0) {
  // TODO Auto-generated method stub
 
 }
 
 @Override
 public void onSurfaceChanged(GL10 arg0, int arg1, int arg2) {
  // TODO Auto-generated method stub
 
 }
 
 @Override
 public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) {
  // TODO Auto-generated method stub
 
 }
 
}
The Renderer Interface provides three methods: onDrawFrame, onSurfaceChanged, and onSurfaceCreated. One thing to note is the Renderer in Android will continually draw frames. It will call onDrawFrame repeatedly, allowing you to perform any animations through the logic in this method. In our example program we will be drawing the same thing over and over again, so it will look like a static image, but it is important to realize that frames are constantly being drawn.
onSurfaceCreated provides a place to do any initialization that doesn’t depend on the screen size or orientation. We won’t be using this method in our example.
onSurfaceChanged is called every time there is a new orientation or screen size (including the first time). We are going to put the following code in this method.
@Override
 public void onSurfaceChanged(GL10 gl, int width, int height) {
  gl.glViewport(0, 0, width, height);
  gl.glMatrixMode(GL10.GL_PROJECTION);
  gl.glLoadIdentity();
  GLU.gluPerspective(gl, 60.0f, (float)width / (float)height, 0.1f, 100.0f);
  gl.glMatrixMode(GL10.GL_MODELVIEW);
  gl.glLoadIdentity();
 }
glViewPort describes the actual flat representation of the 3D objects. If we were using a camera we would call this the photograph. The first two parameters are the x and y position of the bottom left of the viewport. The last two parameters are the width and height of the viewport. We could have a viewport displayed in a window, which would have values other than (0,0) for the position of its bottom left corner and window height and window width instead of screen height and screen width. In our case, however, we are just going to use the screen values.
glMatrixMode tells OpenGL whether your commands are affecting the projection or the models. Using the camera analogy, we need to be able to specify if we are moving the camera or an object in front of the camera.
gl.glMatrixMode(GL10.GL_PROJECTION)
will allow us to issue commands to our camera.
OpenGL provides ways to reuse transformations. Our next command:
gl.glLoadIdentity();
simply resets the projection matrix so we know that no previous commands sent to OpenGL will be compounded with our commands.
Finally, we get to the actual command we are trying to issue to our projection matrix (camera).
GLU.gluPerspective(gl, 60.0f, (float)width / (float)height, 0.1f, 100.0f);
The values that are passed to gluPerspective are our GL10 object, the angle of view, the aspect ratio, and the distance to the near and far planes.  I’ve provided links to information about angle of view and aspect ratio.  The near plane how close an object can be (beyond the near plane) before it is included in the picture.  The far plane is the greatest distance an object can be from the camera before it is no longer included.  You can think of this is setting your range of vision.  The values I picked here are largely arbitrary for everything except aspect ratio.  Feel free to play around with these numbers and note the affect on the rendered image.
Finally we call these lines of code to put us back into ModelView (so our commands will affect the objects instead of the camera) and reset the Model matrix.
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
You will frequently see this pattern in OpenGL where you setup your camera and then leave it fixed while manipulating the objects.

Before we look at our code for onDrawFrame I want to introduce an Enum to handle the different letters we will be drawing. Right click on your Project, go to New, then select Enum. Name the Enum Letters and make sure the package matches the package of your Activity; com.learnandroid.helloworld in my case. Now replace the code in Letters.java with the following code.
package com.learnandroid.helloworld;
 
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
 
import javax.microedition.khronos.opengles.GL10;
 
public enum Letters {
 D (new float[] {
   0.0f, 2.0f,
   0.0f, 0.0f,
 
   0.0f, 0.0f,
   1.0f, 0.0f,
 
   1.0f, 0.0f,
   1.25f, 0.25f,
 
   1.25f, 0.25f,
   1.25f, 1.75f,
 
   1.25f, 1.75f,
   1.0f, 2.0f,
 
   1.0f, 2.0f,
   0.0f, 2.0f
 }),
 E (new float[] {
   0.0f, 2.0f,
   0.0f, 0.0f,
 
   0.0f, 2.0f,
   1.0f, 2.0f,
 
   0.0f, 1.0f,
   1.0f, 1.0f,
 
   0.0f, 0.0f,
   1.0f, 0.0f
 }),
 H (new float[] {
   0.0f, 2.0f,
   0.0f, 0.0f,
 
   0.0f, 1.0f,
   1.5f, 1.0f,
 
   1.5f, 2.0f,
   1.5f, 0.0f
 }),
 L (new float[] {
   0.0f, 2.0f,
   0.0f, 0.0f,
 
   0.0f, 0.0f,
   1.0f, 0.0f
   }),
 
 O (new float[] {
   0.0f, 1.75f,
   0.0f, 0.25f,
 
   0.0f, 0.25f,
   0.25f, 0.0f,
 
   0.25f, 0.0f,
   1.25f, 0.0f,
 
   1.25f, 0.0f,
   1.5f, 0.25f,
 
   1.5f, 0.25f,
   1.5f, 1.75f,
 
   1.5f, 1.75f,
   1.25f, 2.0f,
 
   1.25f, 2.0f,
   0.25f, 2.0f,
 
   0.25f, 2.0f,
   0.0f, 1.75f
 
 }),
 R (new float[] {
   0.0f, 2.0f,
   0.0f, 0.0f,
 
   0.0f, 2.0f,
   1.0f, 2.0f,
 
   1.0f, 2.0f,
   1.5f, 1.5f,
 
   1.5f, 1.5f,
   1.5f, 1.25f,
 
   1.5f, 1.25f,
   1.0f, 1.0f,
 
   1.0f, 1.0f,
   0.0f, 1.0f,
 
   1.0f, 1.0f,
   1.5f, 0.0f
 
 }),
 W (new float[] {
   0.0f, 2.0f,
   0.75f, 0.0f,
 
   0.75f, 0.0f,
   1.0f, 1.0f,
 
   1.0f, 1.0f,
   1.25f, 0.0f,
 
   1.25f, 0.0f,
   2.0f, 2.0f
 });
 
 //Constructor - Load Vertices into a Buffer for OpenGL
 private Letters(float[] vertices)
 {
  this.vertices = vertices;
 
  ByteBuffer buffer = ByteBuffer.allocateDirect(vertices.length * 4);
  buffer.order(ByteOrder.nativeOrder());
  vertexBuffer = buffer.asFloatBuffer();
  vertexBuffer.put(this.vertices);
  vertexBuffer.position(0);
 }
 
 public void draw(GL10 gl)
 {
  gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
 
  gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
 
  gl.glDrawArrays(GL10.GL_LINES, 0, vertices.length / 2);
 
  gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
 }
 
 private float[] vertices;
 private FloatBuffer vertexBuffer;
}
A full discussion on enums in Java is beyond the scope of this tutorial, but I will mention that enums in Java can have most of the features classes have. They can have methods, such as the draw method in our Letters enum. They can have constructors, but only private constructors that get called automatically. The enum class here allows us a simple way to define the points for each letter.
In the constructor I simply take an array of points (or vertices) and put them into a buffer than can be used by OpenGL. Each letter is defined by the set of verticies it passes to the constructor. That is, each pair of numbers represents a point (x, y) in our letter. I have put spaces between each pair of points to make it easier to read, because these define the start and end of a line segment.
Let’s look at how these points are being used in our draw method.
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
These lines are telling OpenGL to use our vertexBuffer for the Vertex Array, and then telling OpenGL to enable the Vertex Array so we can use it.
gl.glDrawArrays(GL10.GL_LINES, 0, vertices.length / 2);
This line does the actual drawing. The first parameter tells it what to draw. GL_LINES will take the first two vertices in the array and draw a line between them, then it will take the next two verticies and draw a line between them, and so on. If there is an odd number of vertices the last vertex will be ignored.
Finally we tell OpenGL that it can disable the Vertex Array when we are done with it.
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
Now let’s go back to HelloRenderer.java and look at the onDrawFrame method.
Here is the code in my onDrawFrame method.
public void onDrawFrame(GL10 gl) {
 
     //Reset Drawing Surface
     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
     gl.glLoadIdentity();
 
     gl.glTranslatef(-4.0f, 5.0f, -20.0f);
     Letters.H.draw(gl);
 
     gl.glTranslatef(2.0f, 0.0f, 0.0f);
     Letters.E.draw(gl);
 
     gl.glTranslatef(1.5f, 0.0f, 0.0f);
     Letters.L.draw(gl);
 
     gl.glTranslatef(1.5f, 0.0f, 0.0f);
     Letters.L.draw(gl);
 
     gl.glTranslatef(1.5f, 0.0f, 0.0f);
     Letters.O.draw(gl);
 
     gl.glTranslatef(-7.0f, -5.0f, 0.0f);
     Letters.W.draw(gl);
 
     gl.glTranslatef(2.5f, 0.0f, 0.0f);
     Letters.O.draw(gl);
 
     gl.glTranslatef(2.0f, 0.0f, 0.0f);
     Letters.R.draw(gl);
 
     gl.glTranslatef(2.0f, 0.0f, 0.0f);
     Letters.L.draw(gl);
 
     gl.glTranslatef(1.5f, 0.0f, 0.0f);
     Letters.D.draw(gl);
 }
The first two lines reset the drawing surface.
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
First clearing the surface to the background color, and then loading the identity matrix so no previous commands will affect our next set of commands.
Next I issue this command:
gl.glTranslatef(-4.0f, 5.0f, -20.0f);
This tells OpenGL to translate (move without rotating) the position where the next object will be drawn by (-4, 5, -20) along the x axis, y axis, and z axis respectively. This is the only time I will translate along the Z axis, but I need to make sure we are between the near and far planes that we set to 0.1 and 100. After performing my translation I will call the draw method on the letter H from my enum, causing it to draw itself.
Letters.H.draw(gl);
Now the next line of code will move the position from where we drew the letter H.
gl.glTranslatef(2.0f, 0.0f, 0.0f);
This is why we called gl.glLoadIdentity() at the very beginning of this method. If we had not done so OpenGL would begin where we left off the last time we drew a frame. Now, however, we use this to our advantage since we want to draw our letter E to the right of our letter H. The rest of this method follow this procedure, moving the position and then calling the draw function on the desired letter. I got the numbers used both for the vertices for each letter and for the distance used in the translations through trial and error, so they are based on how I thought the letters should look rather than any system. You should, by all means, try out different numbers to see what results you get. Here is my final result.
Hello World OpenGL
I know there was a lot of new material here, covered very quickly.  If you have any specific questions please leave a comment and I will be happy to answer to the best of my ability.  Also, since we have reached the end of the Hello World series please let me know if there is any specific topic you would like to see covered in a future article.



Hello Worlds Part 4 – Android Text to Speech

This is part 4 of a multi-part series that looks at some basic Android I/O using simple Hello World programs. At this point I’ll assume you know how to make a new Android Project in Eclipse as that was covered in Part 1.
In this part we are going to look at using Android’s Text to Speech capabilities to get our Android device (or emulator) to actually say “Hello World” to the user.  Text to Speech was introduced in Android 1.6, so when you create your new Android project make sure your minimum required SDK is set to Android 1.6 (or API level 4).  Here is the code we are going to use to make our Android device speak to us.
package com.learnandroid.helloworld;
 
import android.app.Activity;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
 
public class Hello4 extends Activity implements OnInitListener
{
 
 private TextToSpeech tts;
 static final int TTS_CHECK_CODE = 0;
 
 /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        tts = new TextToSpeech(this, this);
    }
 
 @Override
 public void onInit(int initStatus) {
  if (initStatus == TextToSpeech.SUCCESS)
  {
   tts.speak("Hello World", TextToSpeech.QUEUE_FLUSH, null);
  }
 }
}
The first thing you should notice here is the line:
public class Hello4 extends Activity implements OnInitListener
This is different from what we have seen before because it has the extra part at the end “implements OnInitListener”. If you are familiar with Java you know that implements the OnInitListener interface. This will allow our activity to respond to the event triggered when the TextToSpeech engine finishes initializing.  We will use the onInit method for this purpose later in our code.
Inside the class I’ve defined a class variable:
private TextToSpeech tts;
so it will be available to both methods in our activity. Then in the onCreate method I initialize the variable using the TextToSpeech class.
tts = new TextToSpeech(this, this);
If you get an error on this line make sure you have the import android.speech.tts.TextToSpeech; statement at the top. in Eclipse you can use Ctrl + Shift + O to automatically update your import statements.
The constructor for the TextToSpeech class takes two parameters.  The first specifies the the context in which we’re running, and the second is the OnInitListener that should be called.  Since our Hello4 class inherits from Activity we can use it for the context, and since it implements OnInitListener we can use it for the OnInitListener.  Thus I simply provided the keyword “this” for both parameters.
Since we specified our class as the onInitListener to use, the class method onInit will automatically be called when the TextToSpeech class is initialized.
public void onInit(int initStatus) {
  if (initStatus == TextToSpeech.SUCCESS)
  {
   tts.speak("Hello World", TextToSpeech.QUEUE_FLUSH, null);
  }
 }
This method is passed a integer indicating the status after initialization. We will compare this status code to the constant provided by the TextToSpeech class to indicate successful initialization (TextToSpeech.SUCCESS). If Initialization was successful we will call the speak method to actually have your android device speak “Hello World”.
tts.speak("Hello World", TextToSpeech.QUEUE_FLUSH, null);
This method takes three parameters.  The first is the text that should be spoken.  The second is the mode to use for speech.  The mode used for speech can allow you to either speak the text immediately (using QUEUE_FLUSH as we did here) or queue up multiple strings to be read. Since this is intended to be a very simple example we won’t cover this functionality here.  The final parameter allows you to pass a list of additional parameters.  This is also outside the scope of this example.
This is the end of our tutorial on Text To Speech.  Thanks for reading and be sure to check back; there is one more Hello World example forthcoming.

Hello Worlds Part 3 – Using Popup Notifications

This is part 3 of a multi-part series that looks at some basic Android I/O using simple Hello World programs. At this point I’ll assume you know how to make a new Android Project in Eclipse as that was covered in Part 1.  In this part we are going to look at two types of pop-up messages that allow you to communicate with the user outside of the normal program flow.

Toasts

Imagine you are at a large dinner party.  You want to make a toast to your host.  You raise your glass a call out a toast.  Half the people at the party don’t notice (none of the kids at the kids table notice at all), but you aren’t worried about that as you clink glasses with the dozen people who did notice.  The first pop-up we are going to look at is Android’s Toast, which does exactly this.  It allows you to present a message to the user without any confirmation that they noticed the message.  Build a new project using Android SDK 1.1 and update your Activity Java to look like this.
package com.learnandroid.helloworld;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.Toast;
 
public class Hello3 extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        Toast helloToast = Toast.makeText(this, "Hello World", Toast.LENGTH_LONG);
        helloToast.setGravity(Gravity.CENTER, 0, 0);
        helloToast.show();
    }
}
The first line of code we added to the default code generated by the project was this.
Toast helloToast = Toast.makeText(this, “Hello World”, Toast.LENGTH_LONG);
This line of code calls the Static method makeText of the Toast class.  The makeText method returns a Toast object that will display the message you provide, in our case “Hello World”, for the duration you specify.  The two available durations are Toast.LENGTH_LONG and Toast.LENGTH_SHORT.  Once we have our Toast object we can just call the show method to display it on the screen.  Before displaying our toast, however, I thought I would center it on the screen.  The setGravity method lets you tell Android where the toast should be displayed by using one of the Gravity constants, and then specifying an x offset and y offset.  Since I wanted it in the very center of the screen I used Gravity.Center and specified offsets of 0.
helloToast.setGravity(Gravity.CENTER, 0, 0);
You can see a list of available Gravity constants here.  When you run this code you should see a small pop-up with the message “Hello World” appear, and then disappear automatically without any user interaction.  On the next page we’ll look at a pop-up that requires user interaction.

Alerts

When I was a kid I used to love The Great Muppet Caper.  I vividly remember a scene where Gonzo shouts, “Stop the Presses!”  The editor asks him what happened and he says, “I just always wanted to say that.”  If you want a pop-up that makes sure it gets attention (whether something important happened or not) then an Alert might be just what you need.  Let’s update our Java Activity to look like this:
package com.learnandroid.helloworld;
 
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
 
public class Hello3 extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        AlertDialog helloAlert = new AlertDialog.Builder(this).create();
        helloAlert.setTitle("Half Dozen Hello Worlds Demo");
        helloAlert.setMessage("Hello World");
        helloAlert.setButton("Close", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.show();
    }
}
The lines we’ve added are:
AlertDialog helloAlert = new AlertDialog.Builder(this).create();
        helloAlert.setTitle("Half Dozen Hello Worlds Demo");
        helloAlert.setMessage("Hello World");
        helloAlert.setButton("Close", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.show();
The AlertDialog Class makes use of the Builder Class AlertDialogBuilder.  So in the first line when we call:
AlertDialog helloAlert = new AlertDialog.Builder(this).create();
We are using the Builder to return an AlertDialog object to us that is already partially setup for our use.  The next two lines use setTitle and setMessage to set the text displayed to the user in the dialog’s title section and body respectively.  Finally, we need to have a way for the user to actually interact with our alert.  At minimum we should have a close button so the user can make the alert go away. We use setButton, passing it the text that should be displayed on the button, and an event that allows us to perform actions when the button is clicked.  By default, the dialog will be closed on a click event so we don’t need to put any code into our event.  Finally we call the show method, like we did with toast, to display our Hello World pop-up.
While that technically finishes both of our Hello World pop-up programs I want to take a moment to examine the buttons on the AlertDialog.

In SDK 1.1 there were three methods we used to make buttons in our AlertDialog: setButton, setButton2, and setButton3.  In our example here we are only using setButton.  That will give us a single button that is centered horizontally in the AlertDialog.  If we also called setButton2 we would have two buttons side by side with our first button on the left and our second button on the right.  If we include all three buttons like this:
AlertDialog helloAlert = new AlertDialog.Builder(this).create();
        helloAlert.setTitle("Half Dozen Hello Worlds Demo");
        helloAlert.setMessage("Hello World");
        helloAlert.setButton("Button", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.setButton2("Button2", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.setButton3("Button3", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.show();
We would see our first button on the far left, the second on the far right, and the third in the middle like so.
AlertDialog Buttons
If the numbering seems a little arbitrary to you then you may not be alone since these methods were deprecated.  Let’s build this code using a more recent Android SDK.  Right click on your project, and click on Properties.  Click on Android, and click on the check mark next to either Android 1.5, Android 1.6, or Android 2.0.  Then click Apply, and then OK.
17-Project Properties
Go to your code and replace this code block
helloAlert.setButton("Button", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.setButton2("Button2", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.setButton3("Button3", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
With this:
helloAlert.setButton(AlertDialog.BUTTON_POSITIVE, "Button", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.setButton(AlertDialog.BUTTON_NEGATIVE,"Button2", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
        helloAlert.setButton(AlertDialog.BUTTON_NEUTRAL ,"Button3", new DialogInterface.OnClickListener() {
   @Override
   public void onClick(DialogInterface arg0, int arg1) {}
  });
You can see that the new method for adding buttons is to call one single method (setButton) and pass a constant telling Android how to render it. 
This is the end of our introduction to popup notifications, but come back for Part 4 where we will use Text to Speech to make your Android Device actually say “Hello World.”


Hello Worlds Part 2 – Using Layout XML

This is part 2 of a multipart series that looks at some basic Android I/O using some simple Hello World programs.  If you haven’t already read it you should probably start with Part 1.  For this part we are going to create a new project with these settings.
New Project Hello World 2The default Hello2.java will look very similar to the default Hello.java from Part 1.
package com.learnandroid.hellowworld;
 
import android.app.Activity;
import android.os.Bundle;
 
public class Hello2 extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}
This time, however, we are going to find out more about the line setContentView(R.layout.main); In the first example we used setContentView to render our TextView to the screen. By default, however, Android is designed to use XML files to setup your UI. In addition to the src folder, which holds the source code intended to be edited by the developer, there is a gen folder and a res folder. The gen folder holds automatically generated code. If you open this folder and expand the namespace you should see R.java. We’ll come back to R.java in a moment. Before we open that file, lets also expand the contents of the res folder. The res folder holds resources for your android app. By default you should see three folders in here, drawable (which holds an android icon), layout (which contains main.xml), and values (which contains strings.xml). If you open main.xml and click on the main.xml tab you should see the XML that provides your activity its layout.
Hello World 2 Main.xmlWe aren’t going to go into too much detail in this post on the UI XML. I will highlight, however, that the main node is LinearLayout which is a Layout Container that simply lines its children up in order one after another. android:orientation indicates whether they are lined up vertically or horizontally. The one child node we have in LinearLayout in a TextView. This is the same control we used in Part 1, but instead of instantiating it in our code we are using the UI XML to tell Android to provide us with one. The other thing I want you to notice is the line android:text=”@string/hello”. This is going to set the value of our TextView. If you go back to the res folder, open values, open strings.xml and click on the strings.xml tab you can see exactly what value we are putting into our TextView. In strings.xml there is a line
<string name=”hello”>Hello World, Hello2!</string>
so android:text=”@string/hello” will get this value and place it in the TextView that is displayed to the user. Let’s go ahead and change the value to our simple “Hello World” now.
<string name=”hello”>Hello World</string>
Go ahead and save your change and close both strings.xml and main.xml. At this point you are probably wondering how Android gets the information from the XML files and uses them in your program. Go back to the gen folder and open R.java and we’ll see the answer.
11-R JavaWhen R.Java is generated it creates a class for each resource type (drawable, layout, and string) and variables in each class providing access to your resources. so R.layout.main actually maps to your main.xml file via this R Class and the layout inner class. If you don’t exactly understand what is going on here don’t worry. Just remember that R will let you access items in your res folder so the line.
setContentView(R.layout.main);
tells android to use the layout in your main.xml file in res/layout. Since we edited the value in string.xml we should be able to run this program and see our “Hello World” displayed.  Before we move on to part three, let’s modify this program a little bit to show how to access a View element (such as our TextView) in our code.
In order to make our TextView accessible in our code we’re going to start by opening main.xml in /res/layout and editing it like so:
12-Updated Main XML
We’ve removed the line
android:text=”@string/hello”
and replaced it with the line
android:id=”@+id/label”
The @+id tells Android that we are giving the TextView an ID inside of our Application Namespace.  This will allow us to access it in our code.  Let’s open up Hello2.java in our src folder.
package com.learnandroid.hellowworld;
 
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
 
public class Hello2 extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView label = (TextView) findViewById(R.id.label);
        label.setText("Hello World");
 
    }
}
We’ve added two lines of code.  The first:
TextView label = (TextView) findViewById(R.id.label);
uses the findViewById method to get the View, which we cast to a TextView.  Notice how our automatically generated R class automatically provides us with the id we defined in main.xml.  The next line:
label.setText("Hello World");
Simply sets the text to Hello World.  If you run this application now you should see (once again) Hello World on your screen.  That wraps up part 2.  Make sure to check out Part 3 of the series where we explore two kinds of pop-up messages available in Android.

Hello Worlds Part 1 – Your First Android Project

Hello World is a traditional first program because it shows the very simple task of letting the user know you are there (or letting them know that you know that they are there).  It’s simple I/O to send a simple message.  I am going to give a very brief tour of some of the ways you can communicate with a user in Android in this post.  Half Dozen Hello Worlds will highlight some simple I/O and get you started writing your first Android programs.  First let’s go ahead and open Eclipse, where you have already setup Android, and create a new Android Project.  Go to File -> New -> Project.  Select Android Project and click next.
New Project
Fill out the information on the next screen for your project.
New Android Project
You can put whatever you like for the project name, and application name.  Note that I selected Android SDK 1.1.  It is a good idea to select the oldest SDK which has all of the features your program needs, to increase compatibility across devices.  You can think of Create Activity as being similar to create Main Method.  The name of the Activity you place here is the class that will be called when Android tries to run your code.  Once you click Finish you should see the Project.3-src folder If you expand the project, then expand the src folder, and then the package you will see a Java file. This file will be named whatever you called your Activity (so mine is Hello.java).  For simplicity I am going to assume you used the same settings I did when creating your project.  If you did not just substitute your Activity name for mine.  Double click on Hello.java and look at the code that Eclipse has already provided you.
package com.learnandroid.hellowworld;
 
import android.app.Activity;
import android.os.Bundle;
 
public class Hello extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}
Just to give a quick overview.  The package declaration just places this class in the package we specified when we created the project.  The two import statements just import the minimum Android libraries needed for an Activity.  Since Eclipse created this as an Activity it is going to inherit from the Activity class, and we are overriding onCreate, which is called when an Activity is created.  We call onCreate of the superclass.  Finally, we are at the one line of code we really care about right now.
setContentView() tells android what to display to the user.  We are going to change this from the default so that our code looks like this:
package com.learnandroid.helloworld;
 
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
 
public class Hello extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView helloText = new TextView(this);
        helloText.setText("Hello World");
        setContentView(helloText);
    }
}
Let’s briefly look at exactly what we did before moving on.  TextView is an android widget that displays text.  You can think of it as a label, something the user can read but cannot edit.  We instantiated a new TextView.  Then we called the setText method to set the text of it to “Hello World”.
Next we used setContentView to tell Android that our TextView was what we wanted to display in the main part of our application.
Once your code looks like mine, go to the Run Menu and select Run…  In the Run As Popup select Android Application.  Your Android Emulator should launch.  The Emulator will normally start on a lock screen, but as soon as you unlock the device you should see your application launch.
6-Hello 1 Emu
Congratulations!  You have built the first Hello World.  This is not the preferred method for creating an android application, however, because the contents of the View (what the user sees) is being generated directly from the code.  Android prefers that this be separated out of the code and placed into an XML file.  We’ll look at how to do this next in Part 2 of this series.

Lots of Lists: Part 1, Simple List Activity

In most mobile applications, you’re going to be presenting your users a list of something. Most of the time, it’s not as simple as an array of strings; your data may be stored away in a local Sqlite database, or perhaps behind a RESTful API. In any case, we’ll be taking a look at what it takes to begin with a simple ListActivity that can be easily updated later on.
Create a new Android Project (1.1+) and name the activity MyListActivity (or whatever you prefer.) Run the app to ensure you’re working off a valid clean slate.
Next, modify your activity (MyListActivity) to extend the Android class ListActivity and adjust your code to look like the following:
package com.learnandroid.listviewtutorial.simple;
 
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ListAdapter;
 
public class MainListView extends ListActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        ListAdapter adapter = createAdapter();
        setListAdapter(adapter);
    }
 
    /**
     * Creates and returns a list adapter for the current list activity
     * @return
     */
    protected ListAdapter createAdapter()
    {
     return null;
    }
}
So far, so good. We have a method to create our list adapter, so essentially, our onCreate() method is done. Let’s move on to adapters.
Adapters basically glue a collection of objects to an activity, usually by providing a mapping or instructions on how to render each object in the list for the activity. In our case, we have a simple list of strings, so our adapter is pretty straightforward (and in fact, is already provided by Android: ArrayAdapter)
Modify your createAdapter() method to look like the following:
/**
     * Creates and returns a list adapter for the current list activity
     * @return
     */
    protected ListAdapter createAdapter()
    {
     // Create some mock data
     String[] testValues = new String[] {
       "Test1",
       "Test2",
       "Test3"
     };
 
     // Create a simple array adapter (of type string) with the test values
     ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, testValues);
 
     return adapter;
    }
This encapsulates everything we need to get an adapter up and running. Typically here we may make a call-out to data, which may be a Sqlite query or a RESTful GET request, but for now we’re using simple strings. We create an ArrayAdapter and specify three things: Context, Layout, and Data.
The Context is simply where the list came from. In our app it’s simple, but sometimes it can get a little complex when you’re passing intents back and forth across activities, so this is used to keep a reference to the owner activity at all times.
The Layout is where the data will be rendered (and how it will look.) We’re using a built-in Android layout for our needs (simple_list_item_1).
The Data is what will be rendered, and is a simple list of strings.
Final result should look like the following:
package com.learnandroid.listviewtutorial.simple;
 
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
 
public class MainListView extends ListActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        ListAdapter adapter = createAdapter();
        setListAdapter(adapter);
    }
 
    /**
     * Creates and returns a list adapter for the current list activity
     * @return
     */
    protected ListAdapter createAdapter()
    {
     // Create some mock data
     String[] testValues = new String[] {
       "Test1",
       "Test2",
       "Test3"
     };
 
     // Create a simple array adapter (of type string) with the test values
     ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, testValues);
 
     return adapter;
    }
}
Run the app, and you should see something like this:
A screen shot of an example list of strings
A screen shot of an example list of strings
And there you have a nice introduction to a ListActivity that can be expanded upon by focusing on the createAdapter() method. In Part 2, we’ll explore using more complex Object lists and custom Adapters, as well as a brief introduction to the layout files. Thanks for reading!

Popular Posts