September 2011

2 articles in September 2011
Android ListView

Android ListView

So, like I mentioned in my previous blog, I started picking up android recently. It’s been fun. It’s confusing sometimes translating web developer’s framework into Android’s mindset. This is what I learn. My current self-initiated project is to build a room reservation system for Android. Ball State has an online room reservation system, and I’m basically building an android app for it. When someone is searching for a room, basically list of rooms will be displayed. This list has 2 rows(pictured on the right).

I’m so used to being a web developer. To me this looks like a drop down(combo) box where each option has its own unique ID field. Well…. similar, but a bit different in android. This was somewhat confusing to me because each row in drop down box has only single row. In android, a list item can consist of more than 1 row or column, but still uses one single unique identifier.

Let’s get crackin’ from the bottom up.

First of all, you can customize pretty much how everything looks in android. You just need to add an XML file to create a layout. Kinda like the CSS in Android in my opinion. So, let’s define how each row is going to look like. Basically I want each Item to have 2 rows and to look like below.

ListView item

ListView item

The first row is a TextView, the room number posted outside each room in the library. This will be populated based on the search query sent to a web service I wrote. The second row will be the detail of the room, will also be pulled from the web service. I need to tell Android that I want each of my item in a ListView to look exactly like the one on the left when it’s filled with data.

This is the XML layout code. I called mine item_listing.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" 
  android:background="@color/room_listing_background">
	<TextView 
		android:layout_height="wrap_content" 
		android:id="@+id/txtRoom" 
		android:textAppearance="?android:attr/textAppearanceLarge" 
		android:layout_width="fill_parent" 
                android:background="@drawable/room_listing_states"
                android:paddingLeft="@dimen/left_padding" 
                android:textColor="#ffffff" 
                android:textStyle="bold" 
                android:paddingTop="5dp"></TextView>
	<TextView 
		android:layout_height="wrap_content" 
		android:text="TextView" android:id="@+id/txtCapacity" 
		android:background="@drawable/room_listing_states" 
		android:layout_width="fill_parent" 
		android:textColor="#ffffff" 
		android:textAppearance="?android:attr/textAppearanceMedium" 
		android:paddingLeft="15dp" 
		android:paddingBottom="5dp" 
		android:layout_margin="0dp"></TextView>
 </LinearLayout>

Don’t worry about the background=@drawable code above for now. It’s yet another layout(drawable) to tell each item how they should be displayed when they are in clicked mode, hover mode, or regular mode. I’ll post the code for it below.

Okay, I just defined how each item is going to look. Now, I need to define how the screen is going to look. I basically want it to look like the first picture on the right above(Android ListView). The area with white background is 2 TextViews object occupying 2 rows. These 2 rows do not scroll. The red area is a scrollable ListView populated using the XML layout(rule) above for each item. Let’s call this rooms.xml This is the code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" android:background="@color/screen_background">
    <TextView 
    	android:layout_width="wrap_content" 
    	android:layout_height="wrap_content" 
    	android:id="@+id/lblAvailRooms" 
    	android:textColor="@color/label_color" 
    	android:layout_marginLeft="@dimen/left_padding" 
    	android:textAppearance="?android:attr/textAppearanceMedium" 
        android:layout_marginTop="10dp" 
        android:text="@string/strAvailableRooms"/>
 
    	<TextView 
	    	android:layout_width="wrap_content" 
	    	android:layout_height="wrap_content" 
	    	android:text="@string/strTo" 
	    	android:id="@+id/lblTime" 
	    	android:textColor="@color/label_color" 
	    	android:layout_marginLeft="@dimen/left_padding" 
	    	android:layout_marginBottom="@dimen/bottom_padding" 
                android:textAppearance="?android:attr/textAppearanceMedium"/>
    	<ListView 
                android:layout_height="wrap_content" 
                android:layout_width="fill_parent" 
                android:id="@+id/android:list" />
</LinearLayout>

Note the ListView section of the layout. I named the list id list. This is so that I can shift all the data population work to Android, requiring me to code less(very less). This is the awesome part. I don’t need to worry about writing loop to populate the list. I just need to provide the data, and shove it to Android. Let’s do that now. I need to create an activity to display room.xml layout that extends ListActivity. I’m copying only the onCreate method, which shows how to populate the ListView

public class Rooms extends ListActivity {
	private List<HashMap<String, String>> fill;
	private static final String ENC = "UTF-8";
	private int selected;
	@Override
	public void onCreate(Bundle savedInstance){
		super.onCreate(savedInstance);
                //use rooms.xml layout
		this.setContentView(R.layout.rooms);
 
                //set the display text for the white background area. 
                //don't worry about this
		TextView lblAvailRoom = (TextView) findViewById(R.id.lblAvailRooms);
		lblAvailRoom.setText(getResources().getString(R.string.strAvailableRooms) + 
                " " + this.getIntent().getExtras().getString("startDay"));
		TextView lblTime = (TextView) findViewById(R.id.lblTime);
		lblTime.setText(getResources().getString(R.string.strFrom) + 
                " "+ this.getIntent().getExtras().getString("startTime") + 
                " " + getResources().getString(R.string.strTo) + " " + 
                this.getIntent().getExtras().getString("endTime"));
 
                //the rooms parameter passed to this activity contain the result of the web service call.
                //It contains the following string 
                //roomid - the unique identifier of a room that will be used in code
                //name - the actual name of the room
                //capacity - capacity of of the room
               // the are formatted as follow: roomid,name,capacity;roomid,name,capacity
 
 
                //check to see how many room is available 
                //so that i can declare the size of my List
		int length= this.getIntent().getExtras().getString("rooms").split(";").length;
 
		String[] rooms = new String[length];
		rooms=this.getIntent().getExtras().getString("rooms").split(";");
 
		fill = new ArrayList<HashMap<String, String>>();
 
                //I'm using HashMap to store my name-value pair of the available room
		for(int i=0;i<rooms.length;i++){
			String[] temp=new String[3];
			temp=rooms[i].split(",");
			HashMap<String, String> map = new HashMap<String, String>();
			map.put("roomid",temp[0]);
			map.put("name", temp[1]);
			map.put("capacity", temp[2]);
			map.put("max", "Capacity: "+ temp[2] + " People");
 
			fill.add(map);
		}
                //If you don't understand what HashMap does, look it up on google.
                // at this point all my room information is available in a variable called fill
                // all I need to do is create an adapter, fill the adapter with the fill variable,
                // and send the adapter to android to process(to fill the layout with data)
 
               //use simple adapter		
               //note that I tell the adapter to use the variable named "name" and "max"
               //to bing to to text view I specified
               //the "name" variable will be displayed on android id txtRoom
               //in item_listing.xml
               //the "max" variable will be displayed on android id txtCapacity
              // in item_lising.xml
		SimpleAdapter adapter = new SimpleAdapter(this, fill, R.layout.item_listing, 
                    new String[] {"name","max"}, new int[]{R.id.txtRoom,R.id.txtCapacity});
 
              //send it off to android ti display on a layout
		setListAdapter(adapter);
	}
}

Note that the fill variable, and the selected variable are both global within this class. This means I can track which item is selected anywhere in this class. To track which item is selected, onListItemClick abstract method needs to be implemented

	protected void onListItemClick(ListView l, View v, int position, long id){
		super.onListItemClick(l, v, position, id);
		selected=position;				
	}

Now, you can refer to a clicked item from anywhere in this class by calling:

fillList.get(selected).get("name")

or

//this is the unique identifier of a selected item in the list
fillList.get(selected).get("roomid")

That’s pretty much it. The hardest part is to understand it from web developer’s view.

As promised above, this is the XML code that is responsible of how buttons should look in different state:
room_listing_states.xml

 
<?xml version="1.0" encoding="utf-8"?>
<selector
  xmlns:android="http://schemas.android.com/apk/res/android">
	<item android:state_pressed="true">
		<shape>
			<stroke 
				android:width="@dimen/stroke_width"
				android:color="@color/black" />
			<gradient 
				android:startColor="@color/black" 
				android:endColor="@color/fonts" 
				android:angle="270"/>
		</shape>
	</item>
		<item android:state_focused="true">
		<shape>
			<stroke 
				android:width="@dimen/stroke_width"
				android:color="@color/black" />
			<gradient 
				android:startColor="@color/hover_bg" 
				android:endColor="@color/hover_bg"/>
		</shape>
	</item>
	<item>
		<shape>
			<solid android:color="@color/button_background" />	
		</shape>
	</item>
</selector>