The Android Tutorial Series Part 3 – Layout

Short Link: http://wp.me/p5Jvc-9B

My first two tutorials in this series covered a simple HelloWorld and provided an insight into development of a simple android based mobile application (mobile app). In this blog post I intended to skim below the surface and take a look at the various layout formats supported by the Android 4.0 platform. My development platform as discussed in Part 1 HelloWorld blog post remains unchanged.

Android allows definition of its UI in two ways, defining UI elements, their layout declaratively in a XML or programmatically adding UI elements with layout information using Java programming language. I intend to cover both the methods of UI creation. The Android 4.0 platform supports the following layout formats:

  • FrameLayout
  • LinearLayout
  • ListView
  • GridView
  • TableLayout
  • RelativeLayout
  • TabLayout

In the subsequent paragraphs, I will be explaining each layout, its configuration settings and wherever possible detail out how to implement the layout declaratively and programmatically. Before starting, let me describe the common resources for the subsequent discussion. They are colors.xml and strings.xml located within the /res/values folder.

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
        <color name="white">#ffffffff</color>
        <color name="red">#ffff0000</color>
        <color name="black">#ff000000</color>
        <color name="blue">#ff0000ff</color>
        <color name="green">#ff00ff00</color>
        <color name="cyan">#ff00ffff</color>
        <color name="dkgray">#ff444444</color>
        <color name="gray">#ff888888</color> 
</resources>

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, LayoutDemoActivity!</string>
    <string name="app_name">Layout Demonstrator</string>
    <string name="usernamelbl">UserNameLabel</string>
    <string name="usernamefld">usernamefld</string>
    <string name="passwordfld">passwordfld</string>
    <string name="passwordlbl">passwordlbl</string>

    <!-- FrameLayout starts -->    
    <string name="firstview">My First View</string>
    <string name="secondview">My Second View</string>    
    <!-- FrameLayout ends -->    
    
    <!-- LinearLayout starts -->    
    <string name="one">One</string>
    <string name="two">Two</string>
    <string name="three">Three</string>    
    <!-- LinearLayout ends -->   
    
    <!-- TableLayout starts -->    
    <string name="calchint">Enter your input</string>
    <string name="onefigure">1</string>
    <string name="twofigure">2</string>
    <string name="threefigure">3</string>
    <string name="eqfigure">=</string>
    <!-- TableLayout ends -->   
     
    <!-- RelativeLayout starts -->
    <string name="username">User name:</string>
    <string name="password">Password:</string>
    <string name="login">Log In</string>
    <!-- RelativeLayout ends -->
</resources>

I will explain the significance and application of the two XMLs individual entries during the walk thru of individual layout. Let’s look at the first layout, FrameLayout.

FrameLayout

The FrameLayout is the simplest of all layouts. It is designed to contain and display only one UI element. Addition of multiple UI elements will just cause the subsequent elements to overlay above the previous element(s). Following is the source of framelayout.xml which is FrameLayout’s declarative implementation:

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

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/firstview" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/secondview" />
    
</FrameLayout>

The root element FrameLayout defines that UI window will be presented using the FrameLayout format. The attributes layout_width and layout_height prescribe the width and height for the layout container. In the XML we have defined the value of two attributes as ‘fill_parent’ which means that the layout container is expected to cover the available screen real estate. Alternatively the developer is free to define specific dimensions in the following units: px(pixels), dp(density-independent pixels) and sp(scaled pixels based on preferred font size), in(inches) and mm (millimeters). An elaborate explanation on the various units is available here.

Next I have defined a TextView UI element. The layout_height attribute is defined as ‘wrap_content’ which means that the height of the TextView component depends on the length of the component’s text message. The text message is defined by the value of android:text attribute. The value can be the text message string or a pointer to the name attribute of string defined in strings.xml. In our case, it is a pointer to the string named ‘firstview’, the syntax format is @string/{name}. In addition to the firstview TextView component we have defined another TextView component named secondview. The code associated with the second TextView is commented out. This is specifically done to showcase the impact of adding more than one UI components in FrameLayout. Run the code leaving secondview element definition commented. So how do we run code? My Android project name is LayoutDemo. During Android project creation, eclipse creates a Activity class named LayoutDemoActivity. Please find below the source code of LayoutDemoActivity class.

package com.android.layout;

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

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

Change line number 11 to the following:

setContentView(R.layout.framelayout);

To understand intricacies of the generated class R refer to Satya Komatineni’s blog post ‘Understanding R.java’.

The changed source code causes LayoutDemoActivity class to use the framelayout.xml as its content. To run the LayoutDemoActivity class do the following. Within the project, search and open the AndroidManifest.xml. Check if the manifest file has the following entry, if not add it.

        <activity
            android:label="@string/app_name"
            android:name=".LayoutDemoActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Please note one thing. The top element of the manifest file is defined as follows:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.layout"
    android:versionCode="1"
    android:versionName="1.0" >

Refer the package attribute value. The complete class name is derived by concatenating package attribute value of manifest element and value of android:name attribute of activity element; therefore we have ‘com.android.layout’ + ‘.LayoutDemoActivity’ which becomes com.android.layout.LayoutDemoActivity. Hence the activity name attribute always begin with a ‘.’. Do keep this in mind in case you intend to modularize based on package name and keep Activity classes under different packages.

In the Eclipse menu select Run –> Run Configurations. In the left pane within Android application select ‘LayoutDemo'(this can be any name you may have provided). In the right pane under the ‘Android’ tab in Launch Action select the Launch radio button. Within the drop-down choices select ‘com.android.layout.LayoutDemoActivity’. The application should open in the AVD. The text message ‘My First View’ should be displayed. There’s the output.

Frame Layout Example

Uncomment the secondview TextView and run the application again. The text message will appear garbled as the two TextView contents will overlap. Now that we have covered the declarative programming part, let’s look at the programmatic option.

Here’s the source code of FrameLayoutActivity class.

package com.android.layout;

import android.app.Activity;
import android.os.Bundle;
import android.widget.FrameLayout;
import android.widget.TextView;

public class FrameLayoutActivity extends Activity {
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
                
        TextView view1 = new TextView(this);
        view1.setText(R.string.firstview);

        TextView view2 = new TextView(this);
        view2.setText(R.string.secondview);
        
        FrameLayout frame = new FrameLayout(this);
        frame.addView(view1);
        //frame.addView(view2);
        
        setContentView(frame);
    }
}

In Java world environment, the generated R.class is used to reference application resources.
Note that there is android.R class which holds platform specific resource values. The R.class you need will be part of your custom package which in my case is com.android.layout package. The R class reference format is R.{resourceType}.{resourceName}, hence we have R.string.firstview at line number 15 in source code.

Add the entry for FrameLayoutActivity in Android manifest file.

        <activity 
            android:label="@string/app_name"
            android:name=".FrameLayoutActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Run the FrameLayoutActivity class using the same method as mentioned for running LayoutDemoActivity. Instead of selecting LayoutDemoActivity as the launch class select FrameLayoutActivity. In the source code note line number 22 which comments out view2. Uncomment the line and run the application to understand the impact of having more than one UI components in FrameLayout.

LinearLayout

The next layout we look at is LinearLayout. This layout container lays out its children aligned to a particular orientation either horizontal or vertical. The developer needs to define the orientation and the children components are laid out horizontally one besides the other or vertically one below the other.

Here’s how you define LinearLayout declaratively. Refer linearlayout.xml

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

    <TextView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="@string/one" 
        android:background="@color/red"
        android:textColor="@color/cyan"
        android:gravity="left"
        android:textStyle="bold"
        android:layout_gravity="left" 
        android:layout_weight="0"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp" />
       
    <TextView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="@string/two" 
        android:background="@color/green"
        android:textColor="#ff000000"
        android:gravity="center"
        android:typeface="sans"
        android:textStyle="bold|italic"
        android:layout_gravity="center" 
        android:layout_weight="0"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:layout_marginRight="5dp"/>
        
    <TextView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="@string/three" 
        android:background="@color/blue"
        android:textColor="@color/white"
        android:gravity="right"
        android:typeface="serif"
        android:textStyle="italic"
        android:layout_gravity="right" 
        android:layout_weight="0"
        android:layout_marginLeft="5dp"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:layout_marginRight="5dp"/>
</LinearLayout>
  • android:orientation – The orientation of the LinearLayout is determined by the value specified for this attribute. The valid options are vertical and horizontal. For our example we are using vertical orientation.
  • android:layout_width – This attribute defines the width of the TextView. The TextView also defines another attribute named android:width. Apparently both attributes achieve the same thing, defining width dimension of the TextView.
  • android:layout_height – The layout_height defines the height of the TextView component, presently it is defined as ‘wrap_content’, so as to size it to the text contents of the TextView component.
  • android:text – already explained earlier.
  • android:background – This attribute assigns the background color to the TextView. We can assign a color from the application resources colors.xml. The resource reference format is @color/{name}. Alternatively color can be directly assigned as done for textColor of the second TextView, refer line number 26. The color value is a hexadecimal value in the Alpha-Red-Green-Blue (#AARRGGBB) format.
  • android:textColor – This attribute assigns color to the text content of TextView. Value assignment is similar to the background attribute.
  • android:gravity – This attribute defines the orientation of text message vis-a-vis the TextView layout. For our example we have provided left aligned orientation.
  • android:typeface – This attribute defines the font used by the text content of TextView. The valid options are normal, sans, serif, monospace.
  • android:textStyle – This attribute defines the text styling such as bold, italic etc. In case you want to apply two styles together, use the | and assign both values to textStyle attribute. Refer line number 29 for ready reference.
  • android:layout_gravity – This attribute defines the orientation of the TextView component. Note the layout_gravity takes care of the TextView component orientation, while the gravity attribute takes care of TextView’s text content’s orientation, a vital difference. To understand further details refer Sandip Chitale’s blog post.
  • android:layout_weight – This attribute determines how the space leftover after laying out all components is distributed amongst the components. The attribute accepts a numeric value. To get a better insight refer Mayra’s excellent answer in stackoverflow.com.
  • android:layout_marginLeft, bandroid:layout_marginRight,
    android:layout_marginTop, android:layout_marginBottom – These attributes determine the left, right, top and bottom margin settings for the TextView component. The units are same as the ones defined for width or height.

In the example, I have created three TextViews with varying attribute settings to showcase the various configurations. Play around with layout_weight values to understand the impact of this attribute. Here’s the output of the linearlayout.xml:

Linear Layout Example

For programmatic implementation refer to the LinearLayoutActivity class below:

package com.android.layout;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

public class LinearLayoutActivity extends Activity {
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        TextView view1 = new TextView(this);
        view1.setText(R.string.one);
        view1.setBackgroundResource(R.color.red);
        view1.setTextColor(Color.CYAN);
        view1.setGravity(Gravity.LEFT);
        view1.setTypeface(Typeface.DEFAULT_BOLD);
        
        TextView view2 = new TextView(this);
        view2.setText(R.string.two);
        view2.setBackgroundResource(R.color.green);
        //view2.setBackgroundColor(0xffff0000);
        view2.setTextColor(0xff000000);
        view2.setGravity(Gravity.CENTER);
        view2.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD_ITALIC);

        TextView view3 = new TextView(this);
        view3.setText(R.string.three);
        view3.setBackgroundResource(R.color.blue);
        view3.setTextColor(getResources().getColor(R.color.white));
        view3.setGravity(Gravity.RIGHT);
        view3.setTypeface(Typeface.SERIF, Typeface.ITALIC);
        
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
        		LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT);
        layout.setLayoutParams(params);
        
        LinearLayout.LayoutParams params1  = new LinearLayout.LayoutParams(100, 
                ViewGroup.LayoutParams.WRAP_CONTENT,0);
        params1.gravity = Gravity.LEFT;
        params1.weight = 0;
        params1.leftMargin = 5;
        params1.topMargin = 5;
        params1.bottomMargin = 5;
        
        LinearLayout.LayoutParams params2  = new LinearLayout.LayoutParams(100, 
                ViewGroup.LayoutParams.WRAP_CONTENT,0);
        params2.gravity = Gravity.CENTER;
        params2.weight = 0;
        params2.leftMargin = 5;
        params2.topMargin = 5;
        params2.bottomMargin = 5;
        params2.rightMargin = 5;
        
        LinearLayout.LayoutParams params3  = new LinearLayout.LayoutParams(100, 
                ViewGroup.LayoutParams.WRAP_CONTENT,0);
        params3.gravity = Gravity.RIGHT;
        params3.weight = 0;
        params3.leftMargin = 5;
        params3.topMargin = 5;
        params3.bottomMargin = 5;
        params3.rightMargin = 5;
        
        layout.addView(view1, params1);
        layout.addView(view2, params2);
        layout.addView(view3, params3);
        
        setContentView(layout);
    }
}

The following list summarizes relationship between the XML attributes and UI component Class’s methods.

For LinearLayout:

  • android:layout_weight, android:layout_height – The width and height are constructor arguments of inner class LayoutParams of LinearLayout. Refer code between line number 43 and 45.
  • android:orientation – This XML attribute maps to instance method setOrientation on LinearLayout class. Refer line number 41

For TextView:

  • android:text – This attribute maps to instance method setText on TextView. Refer line number 26. The setText method is overloaded and allows two arguments one is an actual text string and the other is reference to application string resource.
  • android:background – This attribute maps to two methods of the TextView namely setBackgroundColor and setBackgroundResource. The first method allows setting of color directly, refer commented line number 28 and second allows pointing to application color resource refer line number 35.
  • android:textColor – This attribute maps to setTextColor method of TextView. The method accepts the actual color value(line number 21, 29) or the application resource color reference (line number 36). Sadly there is no setTextColorResource method available, hence the convoluted way of setting text color using a resource.
  • android:gravity – This attribute maps to setGravity method of TextView. It accepts int argument. Use android.view.Gravity class’s static final int values (line number 22, 30, 37).
  • android:typeface, android:textStyle – This attribute maps to TextView’s setTypeface method. The setTypeface method is overloaded to accept only typeface or a combination of typeface and text style (line number 23, 31, 38).

We have so far covered attributes directly mapping to the TextView class. There are some other XML attributes which do not directly map to the TextView class. For these, an instance of LinearLayout.LayoutParams needs to be created and its appropriate properties need to be set.

  • android:layout_width, android:layout_height, android:layout_weight – These attributes are constructor arguments of the LinearLayout.LayoutParams(line number 43-44). The layout_weight attribute also maps to weight property of LayoutParams class(line number 50).
  • android:layout_gravity – This attribute maps to the gravity attribute of LayoutParams(line number 49, 57). Note this is the gravity orientation for the TextView component within the LinearLayout container.
  • android:layout_margin* – These attributes map to the *Margin properties of LayoutParams class(line number 59 to line number 62).

The association of an instance of LinearLayout.LayoutParams with its component is achieved via the layout’s addView method which accepts the UI component and LayoutParams as its input arguments(line number 73 to 75).

An important note: The dimensional setter methods such as setHeight, setWidth accept int input parameters. The input parameter accept pixel values. The dimension in xml definition were defined in dp. Hence a conversion is necessary. Android provides a ready utility class TypedValue to achieve the conversion. Refer its method applyDimension; here’s a sample for reference. I have purposely chosen to avoid this implementation for code legibility, however in practice it is expected that the developer takes care of this to ensure UI consistency across devices.

ListView

ListView displays a list of scrollable items which the user can select. Typically the selection is used to trigger some subsequent processs or activity. The following example uses a combination of declarative and programming elements to implement Android ListView.

Refer to the listlayout.xml.

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/list">
</ListView>

The ListView is defined in the listlayout.xml. The next step is to provide data to the view. This is achieved by implementing an adapter. Android provides a ready-to-use ArrayAdapter for simple strings etc. Alternatively the developer can subclass BaseAdapter and implement a custom Adapter. My aim is to create a list which displays images and their corresponding names. Find below source code of my custom Adapter CustomImageListAdapter.

package com.android.adapter;

import com.android.layout.R;

import android.content.Context;
import android.graphics.Typeface;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.TextView;

public class CustomImageListAdapter extends BaseAdapter {

	private int[] images = {R.drawable.autumnwalk, R.drawable.bridge, R.drawable.butterfly,
			R.drawable.cheetah, R.drawable.cloud, R.drawable.highway, R.drawable.martini,
			R.drawable.rocksculpture, R.drawable.skyatsunset, R.drawable.slicedorange,
			R.drawable.smoke, R.drawable.tulips};
	
	private String[] imageDesc = { "Autumn Walk", "Bridge",
			"Butterfly", "Cheetah", "Cloud", "Highway", "Martini", 
			"Rock Sculpture", "Sky at Sunset", "Sliced Orange", "Smoke",
			"Tulips"};
	
	private Context ctx = null;
	
	public CustomImageListAdapter(Context context) {
		this.ctx = context;
	}
	
	@Override
	public int getCount() {
		return images.length;
	}

	@Override
	public Object getItem(int arg0) {
		return null;
	}

	@Override
	public long getItemId(int arg0) {
		return 0;
	}

	@Override
	public View getView(int arg0, View arg1, ViewGroup arg2) {
		
		ImageView imgView = new ImageView(this.ctx);
		imgView.setScaleType(ScaleType.FIT_CENTER);
		imgView.setPadding(8, 8, 8, 8);
		imgView.setImageResource(images[arg0]);
		imgView.setAdjustViewBounds(Boolean.TRUE);
		imgView.setContentDescription(imageDesc[arg0]);
		imgView.setMaxHeight(200);
		imgView.setMaxWidth(200);

		TextView tv = new TextView(this.ctx);
		tv.setText(imageDesc[arg0]);
		tv.setMaxHeight(100);
		tv.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
		tv.setGravity(Gravity.CENTER);
		tv.setTextColor(this.ctx.getResources().getColor(R.color.white));
		
		LinearLayout layoutView = new LinearLayout(this.ctx);
		layoutView.setOrientation(LinearLayout.HORIZONTAL);
		LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(150, 150);
		layoutView.addView(imgView, params1);
		LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams(
				LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
		layoutView.addView(tv, params2);
		
		return layoutView;
	}

}

The images array holds image references for our application. All images are courtesy stockvault. The actual images are stored in the res/drawable folder. The image descriptions are held in the imageDesc string array. The CustomImageListAdapter’s constructor accepts Context as input argument.
The overridden getView method is the most crucial implementation of the Adapter.The getView method is responsible for creating the individual row of the ListView.

The getView implementation creates a LinearLayout of horizontal orientation containing two UI components ImageView and TextView. To understand the finer details of ImageView setScaleType method refer this blog post. ImageView’s setImageResource associates image application resource reference to view. The setContentDescription is used to set its description. The rest of the code is self-explanatory or has already been covered in the earlier sections.

For displaying use the following ListViewSupplementActivity class.

package com.android.layout.dynamic;

import com.android.adapter.CustomImageListAdapter;
import com.android.layout.R;

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

public class ListViewSupplementActivity extends Activity {
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.listlayout);
		ListView view = (ListView) findViewById(R.id.list);
		view.setAdapter(new CustomImageListAdapter(this));
		
	}

}

The setContentView method sets the listlayout.xml as the content view. The method findViewById is used to retrieve the ListView component and the view’s setAdapter is invoked to associate the CustomImageListAdapter to the view.

To run select ListViewSupplementActivity in Eclipse –> Run Configurations’s launch option. Ensure that the android manifest file has the following entry:

        <activity 
            android:label="@string/app_name"
            android:name=".ListViewLayoutActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

The generated output is

ListView Example

GridView

The ListView displays a scrollable single column list. GridView on the other hand displays a scrollable grid of m columns and n rows. ListView is one dimensional, GridView are two dimensional. Like the ListView, the declarative portion of GridView is minimal. Refer the gridlayout.xml.

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/grid"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:numColumns="2"
    android:verticalSpacing="10dp"
    android:horizontalSpacing="10dp"
    android:scrollbarStyle="outsideOverlay"
    android:verticalScrollbarPosition="right"
    android:scrollbars="vertical">
</GridView>

The layout_width and layout_height XML attributes are familiar. The andriod:numColumns defines the number of columns of the grid. For our example we are looking for a two column grid, therefore the value for numColumns is 2. The android:verticalSpacing and android:horizontalSpacing attributes define the vertical and horizontal spacing of the GridView container. The rest of the attributes i.e. scrollbarStyle, verticalScrollbarPosition and scrollbars are associated with styling, position and display of the grid scrollbar.

To run the GridView create the GridViewSupplementActivity class.

package com.android.layout.dynamic;

import com.android.adapter.CustomImageAdapter;
import com.android.layout.R;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class GridViewSupplementActivity extends Activity {

	private float positionX = 0f;
	private float positionY = 0f;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.gridlayout);
		
		GridView grid = (GridView)findViewById(R.id.grid);
		grid.setAdapter(new CustomImageAdapter(this));
		
		grid.setOnTouchListener(new OnTouchListener() {
			
			public boolean onTouch(View v, MotionEvent event) {
				
				GridViewSupplementActivity.this.positionX = event.getX();
				GridViewSupplementActivity.this.positionY = event.getY();
				return false;
			}
		});
		
		grid.setOnItemClickListener(new OnItemClickListener() {
	        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
	    		
	        	DisplayMetrics dm = new DisplayMetrics();
	        	GridViewSupplementActivity.this.getWindowManager().getDefaultDisplay().getMetrics(dm);

	        	int yOffset = dm.heightPixels - parent.getMeasuredHeight();
	        	
	        	GridViewSupplementActivity.this.positionY = GridViewSupplementActivity.this.positionY - 2*yOffset; //position X remains unchanged.
	    			        	
	            Toast t = Toast.makeText(GridViewSupplementActivity.this, v.getContentDescription(), Toast.LENGTH_SHORT);
	            t.setGravity(Gravity.LEFT, (int)GridViewSupplementActivity.this.positionX, 
	            		(int)GridViewSupplementActivity.this.positionY);
	            
	            t.show();
	        }
		});
		
		
	}

}

Line number 26 in GridViewSupplementActivity assigns gridlayout.xml as the Activity’s content. The grid is populated using an Adapter CustomImageAdapter. Refer the adapter’s source code.

package com.android.adapter;

import com.android.layout.R;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;

public class CustomImageAdapter extends BaseAdapter {

	private int[] images = {R.drawable.autumnwalk, R.drawable.bridge, R.drawable.butterfly,
			R.drawable.cheetah, R.drawable.cloud, R.drawable.highway, R.drawable.martini,
			R.drawable.rocksculpture, R.drawable.skyatsunset, R.drawable.slicedorange,
			R.drawable.smoke, R.drawable.tulips};
	
	private String[] imageDesc = { "Autumn Walk", "Bridge",
			"Butterfly", "Cheetah", "Cloud", "Highway", "Martini", 
			"Rock Sculpture", "Sky at Sunset", "Sliced Orange", "Smoke",
			"Tulips"};
	
	private Context ctx = null;
	
	public CustomImageAdapter(Context context) {
		this.ctx = context;
	}
	
	@Override
	public int getCount() {
		return images.length;
	}

	@Override
	public Object getItem(int arg0) {
		return null;
	}

	@Override
	public long getItemId(int arg0) {
		return 0;
	}

	@Override
	public View getView(int arg0, View arg1, ViewGroup arg2) {
		ImageView imgView = new ImageView(this.ctx);
		imgView.setScaleType(ScaleType.FIT_END);
		imgView.setPadding(8, 8, 8, 8);
		imgView.setImageResource(images[arg0]);
		imgView.setAdjustViewBounds(Boolean.TRUE);
		imgView.setContentDescription(imageDesc[arg0]);
		imgView.setMaxHeight(100);
		imgView.setMaxWidth(100);
		
		return imgView;
	}

}

The implementation is similar to the earlier CustomImageListAdapter. Instead of having a LinearLayout with ImageView and TextView as its constituents, this adapter only creates an ImageView.

So far the layouts used as examples did not have any dynamic behavior. In grid view I intend to add some dynamic behavior, here on clicking an image, the description of the image will be displayed for a short period of time. To implement this functionality, we have implemented two listeners one is onTouchListener and the other is OnItemClickListener. The OnTouchListener is responsible for gathering X and Y co-ordinate position of the touch. The OnItemClickListener is responsible for creating a Toast which is display the image description at the specified co-ordinates. Note that the onTouch method returns false; this ensures that the event continues to be propogated and the OnItemClickListener can be triggered. The X co-ordinate position is not a problem however I was unable to fix up the Y co-ordinate position. I am sure the computation I have used for Y co-ordinate is screen specific but could not find any screen independent solution. Here are two references I used : 1 and 2.

To run the GridViewSupplementActivity class go to Eclipse Menu –> Run Configurations. Select ‘GridViewSupplementActivity’ in the launch drop down. Note for the class to run the following entry should be present in the android manifest file(AndroidManifest.xml).

        <activity 
            android:label="@string/app_name"
            android:name=".dynamic.GridViewSupplementActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>    

Note the android:name attribute value. If you refer our earlier discussion of the package attribute of the root element manifest, the class name is created appending the package name to the activity attribute name. GridViewSupplementActivity’s complete name is com.android.layout.dynamic.GridViewSupplementActivity. The manifest element’s package attribute value is com.android.layout. Therefore to ensure the validity of the class name the activity attribute name is .dynamic.GridViewSupplementActivity. The same logic applies to ListViewSupplementActivity too.

The output generated is

GridView Example

TableLayout

The TableLayout lays out its consituents into rows and columns. The layout consists of number of TableRow components, each containing one or more Android UI components.
First let’s implement the TableLayout declaratively. Refer the source code of tablelayout.xml

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:stretchColumns="*">
    
    <TableRow>
        
        <TextView
        	android:hint="@string/calchint" 
        	android:gravity="bottom"
        	android:textSize="20dp"
        	android:paddingLeft="5dp"
        	android:paddingTop="0dp"
        	android:paddingRight="0dp"
        	android:paddingBottom="2dp"
        	android:background="@color/white"
        	android:textColorHint="@color/blue"
        	android:textStyle="bold"
        	android:height="40dp"
        	android:layout_marginLeft="5dp"
        	android:layout_marginRight="5dp"
        	android:layout_marginTop="5dp"
        	android:layout_marginBottom="5dp"
        	android:layout_span="3"
        	android:layout_width="fill_parent"/>
        
    </TableRow>
    <TableRow>
        
        <TextView
        	android:text="@string/onefigure"
        	android:textSize="24dp" 
        	android:textStyle="bold"
        	android:background="@color/dkgray"
        	android:textColor="@color/white"
        	android:gravity="center_horizontal"
        	android:layout_marginLeft="5dp"
        	android:layout_marginRight="5dp"/>
        
        <TextView
        	android:text="@string/twofigure"
        	android:textSize="24dp" 
        	android:textStyle="bold"
        	android:background="@color/dkgray"
        	android:textColor="@color/white"
        	android:gravity="center_horizontal"
        	android:layout_marginRight="5dp"/>
        
        <TextView
        	android:text="@string/threefigure"
        	android:textSize="24dp" 
        	android:textStyle="bold"
        	android:background="@color/dkgray"
        	android:textColor="@color/white"
        	android:gravity="center_horizontal"
        	android:layout_marginRight="5dp"/>
        
    </TableRow>
    <TableRow>
        
        <TextView
        	android:text="@string/eqfigure"
        	android:textSize="32dp" 
        	android:textStyle="bold"
        	android:background="@color/gray"
        	android:textColor="@color/white"
        	android:gravity="center_horizontal"
        	android:layout_marginTop="5dp"
        	android:layout_marginRight="5dp"
        	android:layout_column="1" 
        	android:layout_span="2" />
       
    </TableRow>
        
</TableLayout>

In our example we are defining a table with 3 rows and 3 columns. The root element TableLayout has one layout specific attribute stretchColumns. This defines the specific columns which will stretch over the available space in the row. By specifying ‘*’, we have designated all columns to equally use the available space. Alternatively one can specify one or more columns to this attribute. In case of multiple columns use the comma(,) delimiter to specify the columns. In our case ‘*’ can be changed to ‘0,1,2’. Note the column number are zero index based.

Next we have defined the first table row containing one TextView. Most of the TextView XML attributes have already been explained in the previous sections, I will focus on those which are new.

  • android:hint – The text which appears when the TextView is loaded. Usually acts as a suggestion to the application end user.
  • android:textColorHint – Defines the color of the hint text.
  • android:layout_span – In some cases content spans multiple columns. In HTML world we have the colspan which determines the number of columns a cell can span. The layout_span attribute implements the same thing. We have defined layout_span as 3 meaning the TextView will span all the three columns in the table.

The following XML attributes are defined in the 3rd TableRow TextView UI component.

  • android:layout_column – Allows developer to specify a specific column number within which the UI component will be laid out. In our example, the value provided is 1 meaning the TextView of eqfigure will be laid out in the second column as the span is set to 2, it will cover the second and third columns.

To run the tablelayout.xml do the following change in line number 11 of LayoutDemoActivity class and then run the class.

setContentView(R.layout.tablelayout);

Like stretchColumns TableLayout provides shrinkColumns. The shrinkColumns ensures that columns cover reasonable screen real estate. To get more information about TableLayout and also shrinkColumns refer here.

Now that we have covered the declarative aspect of TableLayout, let’s understand how the same can be implemented programmatically.

package com.android.layout;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

public class TableLayoutActivity extends Activity {
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		TableLayout table = new TableLayout(this);
		TableLayout.LayoutParams params = new TableLayout.LayoutParams();
		params.width = TableLayout.LayoutParams.FILL_PARENT;
		params.height = TableLayout.LayoutParams.FILL_PARENT;
		table.setLayoutParams(params);
		table.setStretchAllColumns(Boolean.TRUE);
		
		TableRow displayRow = new TableRow(this);
		
		TextView calculatorDisplay = new TextView(this);
		calculatorDisplay.setHint(R.string.calchint);
		calculatorDisplay.setGravity(Gravity.BOTTOM);
		calculatorDisplay.setTextSize(20);
		calculatorDisplay.setPadding(5, 0, 0, 2);
		calculatorDisplay.setBackgroundResource(R.color.white);
		calculatorDisplay.setHintTextColor(getResources().getColor(R.color.blue));
		calculatorDisplay.setTypeface(null,Typeface.BOLD);
		calculatorDisplay.setHeight(40);
		
		TableRow.LayoutParams rowParams = new TableRow.LayoutParams();
		rowParams.span = 3;
		rowParams.leftMargin = 5;
		rowParams.rightMargin = 5;
		rowParams.topMargin = 5;
		rowParams.bottomMargin = 5;
		
		displayRow.addView(calculatorDisplay, rowParams);
		
		table.addView(displayRow);
		
		TableRow.LayoutParams leftViewParams = new TableRow.LayoutParams();
		leftViewParams.leftMargin = 5;
		leftViewParams.rightMargin = 5;

		TableRow.LayoutParams viewParams = new TableRow.LayoutParams();
		viewParams.rightMargin = 5;

		
		TableRow row1 = new TableRow(this);
		TextView view1 = new TextView(this);
		view1.setText(R.string.onefigure);
		view1.setTextSize(24);
		view1.setTypeface(null,Typeface.BOLD);
		view1.setBackgroundColor(Color.DKGRAY);
		view1.setTextColor(getResources().getColor(R.color.white));
		view1.setGravity(Gravity.CENTER_HORIZONTAL);
		row1.addView(view1, leftViewParams);

		TextView view2 = new TextView(this);
		view2.setText(R.string.twofigure);
		view2.setTextSize(24);
		view2.setTypeface(null,Typeface.BOLD);
		view2.setBackgroundColor(Color.DKGRAY);
		view2.setTextColor(getResources().getColor(R.color.white));
		view2.setGravity(Gravity.CENTER_HORIZONTAL);
		row1.addView(view2, viewParams);
		
		TextView view3 = new TextView(this);
		view3.setText(R.string.threefigure);
		view3.setTextSize(24);
		view3.setTypeface(null,Typeface.BOLD);
		view3.setBackgroundColor(Color.DKGRAY);
		view3.setTextColor(getResources().getColor(R.color.white));
		view3.setGravity(Gravity.CENTER_HORIZONTAL);
		row1.addView(view3, viewParams);

		
		table.addView(row1);
		
		TableRow row2 = new TableRow(this);
		TextView view4 = new TextView(this);
		view4.setText("=");
		view4.setTextSize(32);
		view4.setTypeface(null,Typeface.BOLD);
		view4.setBackgroundColor(Color.GRAY);
		view4.setTextColor(getResources().getColor(R.color.white));
		view4.setGravity(Gravity.CENTER_HORIZONTAL);

		TableRow.LayoutParams calcParams = new TableRow.LayoutParams();
		calcParams.topMargin = 5;
		calcParams.rightMargin = 5;
		calcParams.column = 1;
		calcParams.span = 2;
		
		row2.addView(view4, calcParams);
		
		table.addView(row2);
		
		this.setContentView(table);
	}

}

I will just focus on the specific areas not explained in the previous sections. To implement android:stretchColumns functionality, the TableLayout class provides two methods, one is setStretchAllColumns and the other is setColumnStretchable. Since we needed all columns to be stretchable, we used the first method; refer line number 23 in the source. In case we want specific columns to be made stretchable, we can use the second method, it accepts column number and boolean as input arguments.

TableLayout creation and initialization of its LayoutParams is covered in source code line number 18 to 23. TableRow creation, addition of its constiuents components and their LayoutParams is covered from line number 37-42, 48-53 and 96-100.
The remaining code is self-explanatory. Each TableRow alongwith its LayoutParams is added to the TableLayout via the addView method. TableRow.LayoutParams properties column and span map to XML attributes layout_column and layout_span.

Ensure that TableLayoutActivity is added in the Android Manifest file. Run the class via selecting it in the Launch option to view the results. The output is below

Table Layout Example

RelativeLayout

The RelativeLayout arranges its child components based on component’s layout positioning definition relative to one another. Each component can be laid out above, below, to the left or right of another component. A combination of positional rules such as right of X component and below Y component can also be applied. To gain an understanding let’s look at the declarative implementation of the layout in relativelayout.xml

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

    <TextView
        android:text="@string/username" 
        android:gravity="right"
        android:id="@string/usernamelbl"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:height="40dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp" />

   	<EditText
        android:id="@string/usernamefld"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:height="40dp"
        android:width="200dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"
        android:layout_toRightOf="@string/usernamelbl" />
    
    <TextView
        android:id="@string/passwordlbl"
        android:text="@string/password" 
        android:gravity="right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@string/usernamelbl"
        android:height="40dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp" />
   		
   	<EditText
        android:id="@string/passwordfld"
        android:inputType="textPassword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:height="40dp"
        android:width="200dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"
        android:layout_toRightOf="@string/passwordlbl" 
        android:layout_below="@string/usernamefld"/>
    
   	<Button
   	    android:text="@string/login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
		android:layout_below="@string/passwordfld"
		android:layout_centerHorizontal="true"   	    
   	    />
    	
</RelativeLayout>

Lets’s understand some newly added XML attributes in the relativelayout.xml. We are trying to develop a simple login page with username, password and submit button.

The first TextView a label field for ‘User Name’ text is defined with attributes which we are aware of. Next EditText to capture the actual value of user input is defined. A positional constraint of the EditText is defined via the android:layout_toRightOf attribute. The value is the application string resource reference for the username TextView. Next we define password label which should be positioned below username label. This is achieved by the android:layout_below attribute. The value assigned is the application string resource reference for the username TextView. The EditText component to accept password input is positioned to the right of password TextView and below username EditText component by using two attributes android:layout_toRightOf and android:layout_below. The last component to be covered in the submit button which is purely positioned as center horizontal to be placed below TextViews and EditTexts. I was a bit surprised that RelativeLayout has a special android:layout_centerHorizontal; my thinking was that android:layout_gravity should have sufficed.

To run the application change line number 11 in LayoutDemoActivity class to the following and launch LayoutDemoActivity in Eclipse Run Configurations:

setContentView(R.layout.relativelayout);

Now let’s look at how this can be implemented programmatically. Have a look at the source code of RelativeLayoutActivity.

package com.android.layout;

import android.app.Activity;
import android.os.Bundle;
import android.text.InputType;
import android.view.Gravity;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;

public class RelativeLayoutActivity extends Activity {

	private static int HEIGHT = 40;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	
		RelativeLayout relLayout = new RelativeLayout(this);
		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(300, 600);
		relLayout.setLayoutParams(params);
		TextView userNameLabel = new TextView(this);
		userNameLabel.setText(R.string.username);
		userNameLabel.setGravity(Gravity.RIGHT);
		userNameLabel.setId(R.string.usernamelbl);
		RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 
				LayoutParams.WRAP_CONTENT);
		params1.height = HEIGHT;
		params1.setMargins(10, 10, 10, 10);
		relLayout.addView(userNameLabel, params1);
		
		EditText userNameField = new EditText(this);
		userNameField.setId(R.string.usernamefld);
		RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 
				LayoutParams.WRAP_CONTENT);
		params2.height = HEIGHT;
		params2.width = 200;
		params2.setMargins(10, 10, 10, 10);
		params2.addRule(RelativeLayout.RIGHT_OF, R.string.usernamelbl);
		relLayout.addView(userNameField, params2);
		
		TextView passwordLabel = new TextView(this);
		passwordLabel.setId(R.string.passwordlbl);
		passwordLabel.setText(R.string.password);
		passwordLabel.setGravity(Gravity.RIGHT);
		RelativeLayout.LayoutParams params3 = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 
				LayoutParams.WRAP_CONTENT);
		params3.addRule(RelativeLayout.BELOW, R.string.usernamelbl);
		params3.height = HEIGHT;
		params3.setMargins(10, 10, 10, 10);
		
		relLayout.addView(passwordLabel, params3);

		EditText pwdField = new EditText(this);
		pwdField.setId(R.string.passwordfld);
		pwdField.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
		RelativeLayout.LayoutParams params4 = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 
				LayoutParams.WRAP_CONTENT);
		params4.height = HEIGHT;
		params4.width = 200;
		params4.setMargins(10, 10, 10, 10);
		params4.addRule(RelativeLayout.RIGHT_OF, R.string.passwordlbl);
		params4.addRule(RelativeLayout.BELOW, R.string.usernamefld);
		
		relLayout.addView(pwdField, params4);

		Button submit = new Button(this);
		submit.setText(R.string.login);
		RelativeLayout.LayoutParams params5 = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, 
				LayoutParams.WRAP_CONTENT);
		params5.addRule(RelativeLayout.BELOW, R.string.passwordfld);
		params5.addRule(RelativeLayout.CENTER_HORIZONTAL);
		relLayout.addView(submit, params5);
		
		this.setContentView(relLayout);
		
		
	}
	
}

The layout positioning constraints are defined in RelativeLayout.LayoutParams class’s addRule method. The method accepts position and applicable component reference as its input arguments. Refer the line numbers 41, 50, 64-65, 73-74.

Run the programmatic or declarative option. The output is

Relative Layout Example

TabLayout

There are applications which need the tabs. Individual tab page logically groups together data. Android implements this using the TabActivity. However do note that the class TabActivity has been deprecated in the favor of Fragments. I tried to understand how Fragments work but could not figure it out. I did find this blog post by Evelina Vrabie. I am still digesting the approach which appears a stretch compared to TabActivity. I need to develop the example using TabActivity and explore Fragments later.

The Tab implementation example is done programmatically. Refer the source code of TabsLayoutActivity class.

package com.android.layout;

import com.android.layout.tab.CoffeeShopListActivity;
import com.android.layout.tab.FoodChainListActivity;
import com.android.layout.tab.RestaurantListActivity;

import android.app.TabActivity;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.widget.TabHost;

public class TabsLayoutActivity extends TabActivity {

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		TabHost tabHost = getTabHost();
		Intent intent = null;
		
		intent = new Intent().setClass(this, RestaurantListActivity.class);
		TabHost.TabSpec spec = tabHost.newTabSpec("restaurants");
		spec.setIndicator("Restaurant");
		spec.setContent(intent);
		tabHost.addTab(spec);
		
		intent = new Intent().setClass(this, FoodChainListActivity.class);
		spec = tabHost.newTabSpec("chains");
		spec.setIndicator("Food Chain");
		spec.setContent(intent);
		tabHost.addTab(spec);
		
		intent = new Intent().setClass(this, CoffeeShopListActivity.class);
		spec = tabHost.newTabSpec("shops");
		spec.setIndicator("Coffee Shop");
		spec.setContent(intent);
		tabHost.addTab(spec);
		
		tabHost.setCurrentTab(1);
		
	}
	
}

Note line number 13 where TabsLayoutActivity extends TabActivity. Access to the underlying TabHost instance which provides the tab functionality is obtained via TabActivity’s getTabHost method. I am planning to develop a tabbed application which displays three tab data. The individual tab categories are restaurant, food chain and coffee shop. Each of the tab can be implemented as a View or Activity. To achieve modular design it is recommended that individual Activity per tab are created. Accordingly I have created three Activity classes RestaurantListActivity, FoodChainListActivity and CoffeeShopListActivity. The source code is below:

package com.android.layout.tab;

import android.R;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class RestaurantListActivity extends ListActivity {

	private static final String[] restaurants = new String[] {
		"Golden Punjab", "Shikara", "The Bowl House", 
		"Copper Chimney", "Gajalee"};
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		ListView view = getListView();
		view.setAdapter(new ArrayAdapter<String>(getBaseContext(), R.layout.simple_list_item_1, restaurants));
		
	}
	
}
package com.android.layout.tab;

import android.R;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class FoodChainListActivity extends ListActivity {

	private static final String[] chains = new String[] {
		"Pizza Hut", "Dominos", "Kailas Parbat", "Malgudi's", 
		"Only Parathas", "KFC"};
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		ListView view = getListView();
		view.setAdapter(new ArrayAdapter<String>(getBaseContext(), R.layout.simple_list_item_1, chains));
		
	}	
}
package com.android.layout.tab;

import android.R;
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class CoffeeShopListActivity extends ListActivity {

	private static final String[] coffeeShops = new String[] {
		"Barista", "Cafe Coffee Day"};
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		ListView view = getListView();
		view.setAdapter(new ArrayAdapter<String>(getBaseContext(), R.layout.simple_list_item_1, coffeeShops));
		
	}
	
}

Each of the Activity classes consist of a simple ListView with the some instances of the applicable category.

Refer the following lines in TabsLayoutActivity

		intent = new Intent().setClass(this, RestaurantListActivity.class);
		TabHost.TabSpec spec = tabHost.newTabSpec("restaurants");
		spec.setIndicator("Restaurant");
		spec.setContent(intent);
		tabHost.addTab(spec);

Until now in all our examples we have created a single Activity instance and interacted with it. In this example, we will have to switch between TabActivity and the three Activity instances. This is achieved in Android using an Intent class. The class is responsible for transitioning from one Activity to another. In line number 1 we create an Intent for RestaurantListActivity. Later we create a TabSpec. The indicator attribute holds the text label of the tab. The content holds the presentation data and content. By setting the content to intent, we are assigning RestaurantListActivity class to that tab. The same exercise is done with the other two *ListActivity classes.

Edit the Android Manifest file and key in the following entries.

        <activity 
            android:label="@string/app_name"
            android:name=".TabsLayoutActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


        <activity android:name=".tab.RestaurantListActivity" />
        <activity android:name=".tab.FoodChainListActivity" />
        <activity android:name=".tab.CoffeeShopListActivity" />   

Run the TabsLayoutActivity class using Eclipse’s Run Configurations.

The output is:

Tabs Layout Example

Phew! This has been one long post. This is me signing off.

Published by Vinay Rajadhyaksha

Vinay Rajadhyaksha a.k.a Mr President

21 thoughts on “The Android Tutorial Series Part 3 – Layout

  1. I want to CoffeeShopListActivity to introduce an XML with height of 300dpi.
    I created a main XML. then did I put coffee._layout.xml, where inserted text “hello”:

    package com.android.layout.tab;

    import android.R;
    import android.app.ListActivity;
    import android.os.Bundle;

    public class CoffeeShopListActivity extends ListActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.coffe_layout);
    }

    }

    I fail. Can you help me.
    Thanks a lot!

    1. Hi Gabriel,

      Sorry for the delay in response. You can define exact height using the android:layout_height attribute of ListView element in the coffee_layout.xml. That should do the trick.

  2. it’s condensed, but one of the best tutorial i’ve ever seen for an introduction to android ui. it’s nice that you even go through gridview and tablayout. thank you.

  3. Very nice article for Android UI with great explanation which is easy to understand. Thank you. You are doing very graet job for beginners

  4. Hello,

    Your articles are very nice . But i have one humble request . It would be great if you change the theme of the blog as it is very difficult to read with the current wood background.

    Thank You

    1. @Pushyami,

      I have taken pains to ensure that the theme is suitable from a legibility point of view.

      Maybe you are using a lower screen resolution; I am using 1028 x 768.

  5. He probably has zoomed his text size without zooming the size of images. This causes the off-white parchmenty background to end after a certain distance down the page. After that it’s black text on a dark wood background.

    Maybe you don’t need to support this feature. The new Yahoo Mail, for example, is almost unusable with zoomed text (disappearing buttons etc) and they don’t seem to care. I use it because zooming the whole page means I have to scroll back and forth to see it all.

  6. After looking over a handful of the blog posts on your site, I honestly like your way of writing a blog.

    I bookmarked it to my bookmark site list and will be checking back soon. Please check out my web site as well and tell
    me what you think.

Leave a comment