Creating Android View Adapters Is Easy, but Beware! - No Fluff Just Stuff

Creating Android View Adapters Is Easy, but Beware!

Posted by: Vladimir Vivien on September 9, 2011

Instead of a pure MVC approach, Android provides a simple Adapter API that lets you bridge views with their underlying data source(s).  There are several good examples on the web (search for Android Adapter) on how to use the built-in adapters that come with the android SDK.  These links below provide two examples non-trivial examples from the Android documentations that you should check out if this is your first time using adapters:

Create Your Own Adapters

In non-trivial Android development, chances are you have a complex data graph to maintain states.  Rather then trying to fit your data structure to work with the out-of-the-box adapters, it is relatively easy to just create your own.  As a measure of practice, you should create a custom Adapter implementation for your views (specially ListView instances).  Why?  Your own Adapter class will be more flexible and you will add just enough functionality needed and avoid carrying around bloated code.

A Simple Implementation

The following is derived from a simple implementation which prompted this writing.  I have a simple class Application which I want to bind to a ListView.  Since we are going to use an Adapter we will break down the components as follow: list_layout.xml, item_layout, a data transfer object, and the activity where everything is displayed.

Main Layout – list_layout.xml
This is used for the Activity layout.  It contains the ListView instance that will be bound to the adapter.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <ListView
        android:id="@+id/my_list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:drawSelectorOnTop="false"
        android:textFilterEnabled="false"
        android:stackFromBottom="false"/>
</LinearLayout>

Row Layout – item_layout.xml
This layout is used by the Adapter instance to generate the view used for each item (row) in the ListView.  Use this layout to customize how you want each row in the bounded list view to appear.  In this example, we are going to have a simple TextView instance in each row.

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="12sp">

        <TextView android:id="@+id/data_item"
            android:textSize="18sp"
            android:textStyle="bold"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"/>

</LinearLayout>

Data Transfer Object
This object is part of the internal object graph that is used to maintain state.  It is used by the Adapter as the data source for information bound to the view.

public class MyData{
    private String item;
    public String getItem(){return name;}
    public void setItem(String n){name = n;}
}

The Activity (Abbreviated)
The following shows an abbreviated version of a definition for an Activity class that uses the defined ListView (see above).  Although it looks intimidating, this Activity class contains all the basics of a normal Activity class.  However, part of the definition an Adapter class used to bind data to the ListView (see class MyListAdapter).

public class MyListActivity extends Activity{
    ListView listView;
    List<MyData> dataSource;
    MyListAdapter adapter;

    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
        this.setContentView(R.layout.list_layout);
        listView = (ListView) findViewById(R.id.my_list);
        dataSource = new ArrayList<MyData>();
        adapter = new MyListAdapter(this, R.layout.item_layout, dataSource);
        listView.setAdapter(adapter);
        ...
	}

	// private Adapter for my list
	private static class MyListAdapter extends BaseAdapter {
	    private Activity parentActivity;
	    private int itemLayoutId;
	    private List<MyData> dataSource;
	    private LayoutInflater inflater;

		// constructor for adapter
    	    public MyListAdapter(Activity activity, int layoutId, List<MyData> ds){
    		parentActivity = activity;
    		itemLayoutId = layout;
    		dataSource = ds;
    		inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    	    }

		@Override
		public View getView(int pos, View convertView, ViewGroup parentView){
			View view = null;
			if(convertView == null){
				view = inflater.inflate(itemLayoutId, parentView, false);
				TextView textView = (TextView)view.findViewById(R.id.data_item);
				String data = dataSource.get(pos).getItem();
				textView.setText(data);
			}else{
				view = convertView;
			}
			return view;
		}
	}
}

If you have implemented an Activity before, the beginning should look familiar.  In the onCreate() method, we bind the Activity to the main layout my_list.  Next we pull out the ListView instance from that layout and set its adapter to an instance of the custom adapter class defined by MyListAdapter.

The class MyListAdapter is simple.  It extends the BaseAdapter which is provided by the SDK as a starting point for custom adapters.  You must override public method getView() as shown above.  This method will be called by the View instance, bound to this adapter, to draw the portion of the View that displays the data at the specified position in the data set.  In the snippet, we use and Inflater to reconstruct the View represented by R.layout.item_layout.  Then bind the data to a TextView instance contained in the layout.  The entire inflated View instance is returned by getView() to be used by the parent view.

Beware!

While getView() provides the core logic for your adapter to expose your custom data.  I found that it’s also important to override the following methods shown below.  If not you will spend hours trying to figure out why your list is not displaying properly.

    private static class MyListAdapter extends BaseAdapter{
    ...
        @Override
	public int getCount() {
		return (dataSource != null) ? dataSource.size() : 0;
	}

	@Override
	public Object getItem(int idx) {
		return (dataSource != null) ? dataSource.get(idx) : null;
	}

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

	@Override
	public boolean hasStableIds(){
		return true;
	}

	@Override
	public int getItemViewType(int pos){
		return IGNORE_ITEM_VIEW_TYPE;
	}

	@Override
	public int getViewTypeCount(){
		return 1;
	}
    }

While these methods look simple, they are used by the Adapter API to figure out how render the bounded views.  You can find more information about them from the SDK Android doc page for Adapter.

I hope this was helpful to your Android development efforts!  I have written enough.  Until next time!


Vladimir Vivien

About Vladimir Vivien

Vladimir Vivien is a software engineer living in the United States. Past and current experiences include development in Java and C#.Net for industries including publishing, financial, and healthcare. He has a wide range of technology interests including Java, OSGi, Groovy/Grails, JavaFX, SunSPOT, BugLabs, module/component-based development, and anything else that runs on the JVM.

Vladimir is the author of “JavaFX Application Development Cookbook” published by Packt Publishing. He is the creator of the Groovv JmxBuilder open source project, a JMX DSL, that is now part of the Groovy language. Other open source endeavor includes JmxLogger and GenShell. You can follow Vladimir through his blog: http://blog.vladimirvivien.com/, Twitter: http://twitter.com/vladimirvivien, and Linked In: http://www.linkedin.com/in/vvivien.

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »