Android - List Improvements

Android contains a number of properties that help extend the base implementation of a ListView. However, in my experience, it's necessary to create a custom layout for a nontrivial-looking row. Accordingly, I'll create a custom layout for the list, and I'll name it location_list_row.xml:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="match_parent"
  5. android:layout_height="wrap_content"
  6. android:orientation="horizontal" >
  7.  
  8. <ImageView
  9. android:id="@+id/spotVisibilityImage"
  10. android:layout_width="60dp"
  11. android:layout_height="60dp"
  12. android:layout_marginLeft="5dp"
  13. android:layout_marginTop="5dp"
  14. android:src="@drawable/img_private" >
  15. </ImageView>
  16.  
  17. <LinearLayout
  18. android:layout_width="wrap_content"
  19. android:layout_height="wrap_content"
  20. android:orientation="vertical" >
  21.  
  22. <TextView
  23. android:id="@+id/spotName"
  24. android:layout_width="match_parent"
  25. android:layout_height="25dp"
  26. android:layout_marginTop="5dp"
  27. android:layout_marginLeft="5dp"
  28. android:text=""
  29. android:textSize="20sp" >
  30. </TextView>
  31.  
  32. <TextView android:id="@+id/spotComments"
  33. android:text=""
  34. android:textSize="12sp"
  35. android:layout_marginLeft="5dp"
  36. android:layout_width="match_parent"
  37. android:layout_height="30dp">
  38. </TextView>
  39. </LinearLayout>
  40.  
  41. </LinearLayout>

This layout mimics the contents of the iOS row: An image will appear on the left side of the row, and the name and comments will be vertically spaced on the right side. (Also, I'll default the image to private, but I'll override this when necessary in code.)

Extending ArrayAdapter

For simple text, it's possible to use a default ArrayAdapter instance to manage the contents of each row in the list. However, now that I'm doing something more complex, it's necessary to create a custom adapter. The implementation of the subclass - LocationListRowAdapter.java - is as follows:

  1. package com.crowleyworks.futilefishing.view;
  2. import com.crowleyworks.futilefishing.R;
  3. import com.crowleyworks.futilefishing.model.FishingSpot;
  4.  
  5. import android.content.Context;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.ViewGroup;
  9. import android.widget.ArrayAdapter;
  10. import android.widget.ImageView;
  11. import android.widget.TextView;
  12.  
  13. public class LocationListRowAdapter extends ArrayAdapter<FishingSpot> {
  14. private Context context;
  15.  
  16. public LocationListRowAdapter(Context context, int textViewResourceId) {
  17. super(context, textViewResourceId);
  18. this.context = context;
  19. }
  20.  
  21. @Override
  22. public View getView(int position, View convertView, ViewGroup parent) {
  23. View rowView = convertView;
  24. if (convertView == null) {
  25. LayoutInflater inflater = (LayoutInflater)
  26. context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  27. rowView = inflater.inflate(R.layout.location_list_row, parent, false);
  28. }
  29. TextView tvName = (TextView)rowView.findViewById(R.id.spotName);
  30. TextView tvComments = (TextView)rowView.findViewById(R.id.spotComments);
  31. FishingSpot curSpot = getItem(position);
  32.  
  33. tvName.setText(curSpot.getName());
  34. tvComments.setText(curSpot.getComments());
  35.  
  36. // Change the default graphic if this is a public fishing spot
  37. if (curSpot.getOwner().equals("public")) {
  38. ImageView iv = (ImageView)rowView.findViewById(R.id.spotVisibilityImage);;
  39. iv.setImageDrawable(context.getResources().getDrawable(R.drawable.img_public));
  40. }
  41. return rowView;
  42. }
  43.  
  44. }

Most of this code is fairly straightforward. In fact, the only method of significance is getView(). In this method, I first check to see if Android is attempting to reuse an existing row. If not, then I inflate a new row, and locate the two text views. Next, I get the current fishing spot. I then populate the contents of the two text fields. Lastly, if the view is public, then I update the image associated with the row.

The only remaining change is to update LocationListViewController.java to use the new adapter in the viewDidLoad() method:

  1. @Override
  2. protected void viewDidLoad() {
  3. super.viewDidLoad();
  4. locationList = (ListView)view.findViewById(R.id.locationList);
  5. // adapter = new ArrayAdapter<FishingSpot>(context, android.R.layout.simple_list_item_1);
  6. adapter = new LocationListRowAdapter(context, R.layout.location_list_row);
  7. locationList.setAdapter(adapter);
  8. }

The updated list view looks as follows:

This completes the update of the enhanced Android list view.