Home Archive Tutorials

Getting Started with Android Development – Part 2

This is a blog post by iOS Tutorial Team member Ali Hafizji, an iOS and Android developer living in India. This tutorial is a continuation of Getting Started with Android Development, this site’s first foray into developing for a non-Apple platform! Check it out if you haven’t already. If you were following along last time, […]


  • Other, Other, Other

This is a blog post by iOS Tutorial Team member Ali Hafizji, an iOS and Android developer living in India.

The one quote Steve Jobs never said :-)

The one quote Steve Jobs never said :-)

This tutorial is a continuation of Getting Started with Android Development, this site’s first foray into developing for a non-Apple platform! Check it out if you haven’t already.

If you were following along last time, you created a “Hello World” Android app, got it working on a device or emulator, and then built a simple master/detail app called QuoteReader that displays a list of quotes by our hero, Steve Jobs.

This tutorial takes you through some major improvements to the QuoteReader project, explaining some key Android concepts along the way. By the end, you’ll have learned the following:

  • How layouts work in Android.
  • The most common XML layout attributes and their uses.
  • How to modify QuoteReader to allow quote edits, as well as full-size images.
  • How to allow the user to rate each quote.
  • How to add and delete quotes.

Getting Started: Understanding Layouts

First I’m going to introduce a key concept for Android development that we did not cover in Part One.

Layouts are Android’s solution to the variety of screens that come on Android devices. These screens can have different pixel densities, dimensions, and aspect ratios.

Yes that’s right – iOS developers have it a lot easier with just the iPad and iPhone screen dimensions to worry about! :]

Typical Android devices even allow changing the screen orientation (portrait or landscape) while applications are running, so the layout infrastructure needs to be able to respond on the fly. Layouts are intended to give developers a way to express the physical relationship of views as they are drawn on the screen.

Layouts in Android are in the form of a tree, with a single root and a hierarchy of views. Look back at any of the XML layout files in the previous tutorial, and you’ll see that the XML tags create such a hierarchy, with a screen layout as the root of the tree. Each view in the tree is termed the parent of the views it contains and the child of the view that contains it.

When the layout is inflated by Android (a fancy way of saying converting from XML to actual in-memory view objects), it goes through two phases:

  • Measure phase: During this phase, Android traverses the layout tree from top to bottom, and each view declares the amount of vertical height and horizontal width it needs to display itself in the final display.
  • Layout phase: This phase is also a top-down traversal, but each view now positions each of its children in the size declared by it during the measure pass.

Once both phases are complete, the views are drawn on the screen.

Don’t worry if you’re still a bit confused how this works in practice – you will see soon! :]

Commonly-Used XML Attributes

Learning the exact use of all XML attributes can seem a bit daunting at first. It definitely takes some time to master the ins-and-outs of layout parameters. In this section, I’ll discuss some of the most widely-used layout parameters and their exact uses.

First of all, layout attributes that start with “layout_” have to do with a view’s LayoutParams, which are used by views to tell their parents how they should be… yep, laid out. :]

In other words, these attributes are used to set the size of the view during the measure phase of layout inflation.

The most commonly-used of these attributes are layout_width and layout_height, as they are used to define the size of each view. The most commonly-used values for these two attributes are:

  • match_parent and fill_parent: These set the dimension of the view to be as big as the parent. We used this in main.xml and quote_detail.xml already.
  • wrap_content: This sets the dimension to just enough to fit its contents. We used this in quote_detail.xml already.

The easiest way to understand this is visually. So here’s an example of a button with layout_width set as fill_parent:

And here’s an example of a button with layout_width set as wrap_content:

Next, have a look at the layout_gravity and gravity attributes. These two can be quite confusing initially. Just remember that the one beginning with “layout” has to do with the position of the view in the parent. Hence layout_gravity defines how a view will be positioned in the parent view, while gravity simply defines how the content of the view will be placed within itself.

The image below has layout_gravity set to center_horizontal:

The image below has gravity set to left:

Next is layout_weight. While you won’t be using this attribute in any of the views that you’ll create in this tutorial, you should know about it. This parameter is only used with LinearLayouts and it allows you to prioritize the importance of child views.

For example, if you have multiple views in a vertical LinearLayout, you can define how each of the views will take up extra space so that the entire screen is filled. This is really useful. It means you don’t have to define fixed sizes for each child – instead, the Android system handles everything for you.

The image below has three buttons placed in a vertical LinearLayout, but none have a layout weight assigned:

Whereas in the next image, the three buttons have been given weights of 1, 2 and 3, respectively:

Changing the Data Source

Before you use your new knowledge of layouts to improve the QuoteReader app, you need to make a few architectural changes to the QuoteReader.

First, you need to create a class that contains the data of each and every item in your list. In its present state, QuoteReader maintains three different ArrayLists for the thumbnails, quotes and full-size images. The problem with this approach is that there is no correlation between any of the items. That is, if I remove a thumbnail from the thumbnails ArrayList, it won’t remove the corresponding quote from the quotes ArrayList.

To correct this, first create a separate package to maintain your data source. Right-click on the src folder and select New->package. Name the package “com.tutorial.quotereader.datasource.” Drag the DataSource class into this package, and click OK when the popup appears.

Now create a new class for your item. Right-click on the new package and select New->Class. Name the class DataSourceItem, and click Finish. This class will be responsible for holding the thumbnail (bitmap), full-size image (bitmap), quote (string) and the rating (float value) of that quote.

Add the following private variables and getters/setters to the class:

private Bitmap mThumbnail;
private Bitmap mHdImage;
private String mQuote;
private float mRating;
public Bitmap getmThumbnail() {
    return mThumbnail;
public Bitmap getmHdImage() {
    return mHdImage;
public String getmQuote() {
    return mQuote;
public float getmRating() {
    return mRating;
public void setmRating(float mRating) {
    this.mRating = mRating;
public void setmHdImage(Bitmap mHdImage) {
    this.mHdImage = mHdImage;
public void setmQuote(String mQuote) {
    this.mQuote = mQuote;

You might get some errors for a missing import for the Bitmap class – to resolve this, go to Source\Organize Imports and it will automatically add the required import. If you ever get missing imports in the rest of this tutorial, you can use this same technique. Handy, eh?

Next, add two constructors. The code below should be quite self-explanatory:

public DataSourceItem() {
    mQuote = "New Quote is added";

public DataSourceItem(Bitmap thumbnail, Bitmap hdImage, String quote) {
    if(thumbnail == null || hdImage == null || quote == null)
        throw new IllegalArgumentException();
    mThumbnail = thumbnail;
    mHdImage = hdImage;
    mQuote = quote;

    mRating = 2.5f;

Now that you have your item ready, you need to modify the DataSource class so that instead of having three ArrayLists, you have just one, and each object in the array list is of type DataSourceItem.

Open DataSource.java and replace the contents with the following:

package com.tutorial.quotereader.datasource;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.drawable.BitmapDrawable;

import com.razeware.QuoteReader.R;

public class DataSource {

    private Context mContext;
    private ArrayList<DataSourceItem> mItemsData;

    public DataSource(Context context) {
        this.mContext = context;
        mItemsData = new ArrayList<DataSourceItem>();

    public ArrayList<DataSourceItem> getmItemsData() {
        return mItemsData;

    private void setupItemsData() {
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_1)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_1)).getBitmap(), mContext.getResources().getString(R.string.quote_1)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_2)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_2)).getBitmap(), mContext.getResources().getString(R.string.quote_2)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_3)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_3)).getBitmap(), mContext.getResources().getString(R.string.quote_3)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_4)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_4)).getBitmap(), mContext.getResources().getString(R.string.quote_4)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_5)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_5)).getBitmap(), mContext.getResources().getString(R.string.quote_5)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_6)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_6)).getBitmap(), mContext.getResources().getString(R.string.quote_6)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_7)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_7)).getBitmap(), mContext.getResources().getString(R.string.quote_7)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_8)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_8)).getBitmap(), mContext.getResources().getString(R.string.quote_8)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_9)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_hd_9)).getBitmap(), mContext.getResources().getString(R.string.quote_9)));
        mItemsData.add(new DataSourceItem(((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.steve_10)).getBitmap(), 
                ((BitmapDrawable)mContext.getResources().getDrawable(R.drawable.apple_hd)).getBitmap(), mContext.getResources().getString(R.string.quote_10)));

    public int getDataSourceLength() {
        return mItemsData.size();


Now you are going to change how the data model is handled across different activities. In the earlier tutorial, you did this by creating new DataSource objects in each activity. This approach will not work any longer, because you’re going to modify the data and you want it to be same across all activities. To do this, you’ll use the singleton design pattern.

Add the following to DataSource.java:

private static DataSource mDataSource;
public static DataSource getDataSourceInstance(Context context) {
	if(mDataSource == null)
		mDataSource = new DataSource(context);
	return mDataSource;

Going forward, you will use only this static method to get a reference to the data source.

Editing Quotes

When it comes to editing a quote in QuoteReader, there are two steps. The first is changing the actual text of the quote. The second is changing the full-size image displayed above the quote.

Step 1: Editing the Text

First and foremost, to edit a quote you need to use EditText, which is simply an editable version of TextView. To use EditText, first open res\layout\quote_details.xml and change the TextView to EditText.

We’ll also make some tweaks to some of the parameters and change the layout to a LinearLayout. Modify the file so the final layout looks like this:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="fill_parent" >
            android:layout_marginLeft="5dip" />        

OK, time to test this out! First make some changes to QuoteDetail.java:

// Change mQuote from TextView to EditText
private EditText mQuote;

// Delete line creating mDataSource variable and add this instead
private DataSourceItem mItem;

// Change cast of mQuote to an EditText
mQuote = (EditText) findViewById(R.detail.quote);

// Replace lines setting imageView and Quote with this
mItem = DataSource.getDataSourceInstance(this).getmItemsData().get(mPosition);

And make the following changes to QuoteReaderActivity.java:

// Replace line creating mDataSource in QuoteAdapter constructor
mDataSource = DataSource.getDataSourceInstance(mContext);

// Replace lines setting thumbnail and quote in getView
thumbnail = (ImageView) convertView.findViewById(R.list.thumb);
quote = (TextView) convertView.findViewById(R.list.text);

Now compile and run the program. You’ll notice that when you enter the detail page, you’re presented with a keyboard to enter text for the quote. You can type whatever you like, but if you navigate back to the QuoteReader activity (by tapping your back button on your device), you’ll notice your changes didn’t actually take effect.

To make the changes permanent, you need to modify the entry in the data source. To do this you need to create a listener to give a callback as soon as the user has finished editing the text. Once you know that the user has finished editing, you can change the quote in the respective item of the data source.

Add the following lines to QuoteDetail.java at the end of the onCreate method:

mQuote.addTextChangedListener(new TextWatcher() {

    public void onTextChanged(CharSequence s, int start, int before, int count) {

    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {


    public void afterTextChanged(Editable s) {


Notice the addition of TextChangedListener to EditText. This listener gives three callbacks, but the most significant is onTextChanged, which is fired as soon as the user finishes editing the quote.

Build and run the project. Try editing a quote. Now you’ll notice that the quotes list isn’t updating. That is, you can edit a quote from the detail page, and the edits will be preserved there, but when you return to the quotes list you will see only the original, unchanged quotes.

The problem is that the list adapter does not realize the data source has changed. So all you need to do is tell the list adaptor about the new data set and the list view will be refreshed.

To do this add, implement the onResume method in QuoteReaderActivity.java like the following:

protected void onResume() {
    if(mListView != null) {
        QuoteAdapter adapter = (QuoteAdapter) mListView.getAdapter();

Build and run the project. Everything should now work correctly!

Modifying text in a list view on Android

Step 2: Editing Full-Size Images

To edit the image shown to the user in the detail page, you’re going to allow the user to navigate to the gallery app, select an image and use that image in place of the one shown. This is why you’re using bitmaps instead of resource ids in the DataSourceItem class.

First set up a click listener to listen for click events on the ImageView.

Make the following changes to QuoteDetail.java:

Add the following lines to the QuoteDetail class in the onCreate method:

// Add new private constant variable
private static final int SELECT_PHOTO = 100;

// Add to end of onCreate
mImageView.setOnClickListener(new View.OnClickListener() {

    public void onClick(View v) {
        Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
        startActivityForResult(photoPickerIntent, SELECT_PHOTO);

To launch the gallery app, you first send out an intent. Notice the use of “ACTION_PICK ” to create the intent. This will launch the gallery app such that it will allow the user to pick a single image. The type of the intent is also set to “image/*” to specify the type of data to return: in this case, just an image.

Note: If the user has any other app that can handle the ACTION_PICK intent and return an image, Android will display a selection menu so that the user can choose which app to use.

A function called onActivityResult is called automatically when the user picks an image. You need to override that method to get the image the user picked to display.

So add the following lines to QuoteDetail.java:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode) { 
    case SELECT_PHOTO:
        if(resultCode == RESULT_OK){  
            Uri selectedImage = data.getData();

            try {
                InputStream imageStream = getContentResolver().openInputStream(selectedImage);
                Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream);
            } catch (FileNotFoundException e) {

The Intent that is passed to onActivityResult can be used to get the URI of the selected image. A URI is a unique ID that can be used to identify a resource. The code above uses the URI to get the bitmap of the image, and that bitmap is set as the source of your ImageView. The same bitmap is also set as the full-size image to be used for the quote in the DataSource.

Time to build and run again! You should now be able to edit both the text and the image of each quote.

Adding Rating Bars

Android has a view called RatingBar, provided by the SDK. You’re now going to take advantage of it.

Open the quote_detail.xml file and add the RatingBar view. The numStars parameter indicates the total number of stars used by the rating bar. Set it to 5.

The layout file contents are show below.

<?xml version="1.0" encoding="utf-8"?>
			android:layout_marginLeft="5dip" />
		    android:numStars="5" />
			android:textSize="24dip" />

Then open up QuoteDetail.java and make the following changes:

//Create a new variable for this view
private RatingBar mRatingBar;

// Add the following lines to the end of the onCreate method:
mRatingBar = (RatingBar) findViewById(R.id.rating_bar);
mRatingBar.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() {

    public void onRatingChanged(RatingBar ratingBar, float rating,
            boolean fromUser) {
        //write the rating back to the data source :]

Here you find the view by the ID you set up in the XML file, set the initial rating, set up an event listener to detect when the rating changes, and update the view correspondingly.

Build and run. You should now have an awesome-looking rating bar for each quote!

Adding New Quotes

To add a new quote, you first need to create an options menu. An options menu is what the user sees after pressing the menu button in Android. All basic activity actions and navigation items should be placed in an options menu.

When the Android system creates an options menu for the first time, it calls your activity’s onCreateOptionsMenu method. So that’s what you’re going to override to create your menu.

Open the QuoteReader activity and make the following changes:

// Add to top of QuoteReaderActivity
private static int CREATE_QUOTE_ID = 1;

// Add new method to QuoteReaderActivity
public boolean onCreateOptionsMenu(Menu menu) {
    	menu.add(Menu.NONE, CREATE_QUOTE_ID, Menu.NONE, "Create new quote");
	return true;

The method returns a menu object. Notice that you’re adding an entry to the menu object. The add method takes the following arguments:

  • groupId: The group identifier that this item should be part of. Use this to group a number of options together. Since you have only one, use NONE.
  • itemId: The unique ID given to each item in the menu. You’re using a static int variable with value 1.
  • order: The order for the item.
  • title: The string used to show the title of the menu item.

Build and run. You should now see an options menu when you press the menu button.

Adding an options menu in Android

Now that you’ve got your options menu, you need to get the an event when the user taps the “Create new quote” menu item.

The Android system includes a callback called onOptionsItemSelected, used when a menu item is tapped. That’s what you’ll be using.

Add the following lines to the QuoteReader activity:

public boolean onOptionsItemSelected(MenuItem item) {
    if(item.getItemId() == CREATE_QUOTE_ID) {
        //create an entry into the data set
        //and call data set changed
        DataSource dataSource = DataSource.getDataSourceInstance(this);
        dataSource.getmItemsData().add(new DataSourceItem());
        QuoteAdapter adapter = (QuoteAdapter) mListView.getAdapter();
        return true;
    return super.onOptionsItemSelected(item);

The code checks if the menu item you created has been tapped, and if so, creates a new quote. To add a new quote, you only need to create a new entry in your data source and tell the adapter that the data set has changed.

Build and run the project, and see if when you create a new quote, a new entry is created in the list with default values. Note you’ll have to scroll down to the bottom of the list to see the new entries.

Adding new entries onto a list in Android

Deleting Quotes

To delete a quote you’re going to use a context menu. Think of a context menu as the menu you see when you right-click on an item on any operating system. The menu you see contains options that are very specific to that one item: that’s a context menu.

In this case, your QuoteReader will show a context menu when the user long-presses on a particular item in the quotes list. The first step to implementation is registering the view that will show the context menu. To do this, add the following line of code to the onCreate method of the QuoteReader activity.


Now that your list is registered, whenever the user long-presses on an item in the list, the system will give a callback called onCreateContextMenu. Now you’ll override that method and add code to create the menu.

public void onCreateContextMenu(ContextMenu menu, View v,
        ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);       

Note you’ll get an error because we haven’t added the string for the context_menu_title yet, so add this to your res\values\strings.xml file:

<string name="context_menu_title">Menu</string>

This time, instead of creating a menu using the add method, you’ll use XML. So create a new XML file in the res/layout directory and name it context_menu.xml. Here’s how the file should look:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/delete" android:title="Delete"/>

You’ll use this XML file in your onCreateContextMenu and attach it to the menu object. Use the MenuInflator class to do this. Add the following lines to the end of the onCreateContextMenu method:

MenuInflater menuInflator = getMenuInflater();
menuInflator.inflate(R.layout.context_menu, menu);

Build and run. Now when you long-press on any list element, you should see the menu you just created.

Adding a context menu in Android

To capture the event when the user taps on your menu item, you need to override another function called onContextMenuItemSelected. The code below deletes the item from the data source and tells the adapter to refresh the list.

public boolean onContextItemSelected(MenuItem item) {
    AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
    if(item.getItemId() == R.id.delete) {
        QuoteAdapter adapter = (QuoteAdapter) mListView.getAdapter();
        return true;
    return super.onContextItemSelected(item);

Give it a final build and run. You should now have a completely functional app with full editing, adding, and deleting support!

Where to Go From Here?

Here is an example project with all of the code from the above tutorial.

One thing I didn’t cover in this tutorial is how to edit the thumbnails. This might be a fun thing to try to add on your own if you want to play around with this some more!

Please let me know if you have any questions regarding that, or anything else in this tutorial. Also, please let me know if you’d like to see more Android tutorials on this site (and what you’d like them to cover if so!) :]

This is a blog post by iOS Tutorial Team member Ali Hafizji, an iOS and Android developer living in India.




More like this