SimpleAdapter ViewBinders
Used a SimpleAdapter.ViewBinder the other day. ViewBinders allow another way of attaching data to an Adapter. I needed to capture an onClick event from a row in a ListActivity and fire a corresponding Intent to open a new Activity; basically mimicking the behavior of the API Demos Google provides in the Android SDK.
I could have used the onListViewClick method then cross referenced a separate list to determine the proper Activity to launch. This seems kind of fragile to me since you are relying on two separate lists, and I really wanted to link directly to the TextView representing each row of the ListActivity. By extending TextView I created a class which accepts an Intent to launched via the setOnClickListener.
ToolNameView.java
public class ToolNameView extends TextView
{
private Intent _intentToLaunch;
public ToolNameView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
setupView();
}
public ToolNameView(Context context, AttributeSet attrs)
{
super(context, attrs);
setupView();
}
public ToolNameView(Context context)
{
super(context);
setupView();
}
private void setupView()
{
this.setOnClickListener( new OnClickListener()
{
public void onClick(View v)
{
getContext().startActivity( _intentToLaunch );
}
});
}
public void setIntentToLaunch( Intent intent )
{
_intentToLaunch = intent;
}
}
A layout file defines the rows of the ListActivity
layout/row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/vw1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.mgm.android.toolbox.ToolNameView
android:id="@+id/text1"
android:textSize="16sp"
android:textStyle="bold"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="3dip"/>
</LinearLayout>
A SimpleAdapter is used within the ListActivity to link the data to each row of the ListActivity. This is where it gets a little tricky…as stated in the SimpleAdapter API
You can specify the data backing the list as an ArrayList of Maps. Each entry in the ArrayList corresponds to one row in the list. The Maps contain the data for each row. You also specify an XML file that defines the views used to display the row, and a mapping from keys in the Map to specific views.
Things didn’t truly came together for me until I had the entire code set complete and I could look back and really understand how the ListArray filled to the ListActivity through the Map. I’ll do my best to explain how I understand it.
I defined an ArrayList called _activityNames with a mapping of String to a class called ToolboxRow.
ArrayList<HashMap<String, ToolboxRow>> _activityNames;
Each ToolboxRow holds the name of the Activity and the Intent to trigger when the TextView is clicked on.
class ToolboxRow
{
private Intent _intentToLaunch;
private String _name;
public ToolboxRow( Intent intentToLaunch, String name )
{
_intentToLaunch = intentToLaunch;
_name = name;
}
public Intent getIntentToLaunch()
{
return _intentToLaunch;
}
public String getName()
{
return _name;
}
}
A private method loads the name and Intent into a ToolboxRow, which is placed in a HashMap keyed with a pre-defined String called ACTIVITY_NAME_ENTRY.
/**
* DON'T FORGET TO ALSO ADD ACTIVITY TO MANIFEST!!
*
* @param name
* @param intentToLaunch
*/
private void addActivityToList( String name, Intent intentToLaunch )
{
HashMap entry = new HashMap();
ToolboxRow row = new ToolboxRow( intentToLaunch, name );
entry.put( ACTIVITY_NAME_ENTRY, row );
_activityNames.add( entry );
}
Finally, the SimpleAdapter.ViewBinder is created via a private class within the ListActivity
class ToolBinder implements ViewBinder
{
public boolean setViewValue(View view, Object data, String textRepresentation)
{
ToolNameView tool = (ToolNameView)view;
ToolboxRow row = (ToolboxRow)data;
tool.setIntentToLaunch( row.getIntentToLaunch() );
tool.setText( row.getName() );
return true;
}
}
As can be seen, the ViewBinder will be passed a View and Object, in my application these will be a ToolNameView and a ToolboxRow, respectively. The beauty of the ViewBinder is that it gives me a means to load each ToolNameView with the data from ToolboxRow. The following defines the SimpleAdapter which passes the View and Object to the ViewBinder.
SimpleAdapter toolsAdapter =
new SimpleAdapter(
this,
_activityNames,
R.layout.row,
new String[] { ACTIVITY_NAME_ENTRY },
new int[] { R.id.text1 } );
toolsAdapter.setViewBinder( new ToolBinder() );
These couple of lines perform all the magic to hook the data in the ListArray to the View for each row. That’s a lot of concepts to pack together and I’ll probably have to review this again the next time I use SimpleAdapter.ViewBinders, but hopefully it gives you a little more info if you choose to use ViewBinders in your next ListActivity. The following shows the entire Toolbox class which extends ListActivity.
public class Toolbox extends ListActivity
{
private static final String ACTIVITY_NAME_ENTRY = "activity_name";
ArrayList<HashMap<String, ToolboxRow>> _activityNames;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// define the list which holds the information of the list
_activityNames = new ArrayList<HashMap<String, ToolboxRow>>();
addActivityToList( "Data Roaming", new Intent( this, DataRoamingSetting.class ) );
addActivityToList( "Expanding Example", new Intent( this, Expando.class ) );
SimpleAdapter toolsAdapter =
new SimpleAdapter(
this,
_activityNames,
R.layout.row,
new String[] { ACTIVITY_NAME_ENTRY },
new int[] { R.id.text1 } );
toolsAdapter.setViewBinder( new ToolBinder() );
setListAdapter( toolsAdapter );
}
/**
* DON'T FORGET TO ALSO ADD ACTIVITY TO MANIFEST!!
*
* @param name
* @param intentToLaunch
*/
private void addActivityToList( String name, Intent intentToLaunch )
{
HashMap<String, ToolboxRow> entry = new HashMap<String, ToolboxRow>();
ToolboxRow row = new ToolboxRow( intentToLaunch, name );
entry.put( ACTIVITY_NAME_ENTRY, row );
_activityNames.add( entry );
}
/**
* Binds the ListView row to the Intent to launch when the row is touched
*/
class ToolBinder implements ViewBinder
{
public boolean setViewValue(View view, Object data, String textRepresentation)
{
ToolNameView tool = (ToolNameView)view;
ToolboxRow row = (ToolboxRow)data;
tool.setIntentToLaunch( row.getIntentToLaunch() );
tool.setText( row.getName() );
return true;
}
}
class ToolboxRow
{
private Intent _intentToLaunch;
private String _name;
public ToolboxRow( Intent intentToLaunch, String name )
{
_intentToLaunch = intentToLaunch;
_name = name;
}
public Intent getIntentToLaunch()
{
return _intentToLaunch;
}
public String getName()
{
return _name;
}
}
}

June 14th, 2009 at 3:08 am
Hi. Thanks. It was great. Very useful and easy.