Skip to content

MultiChoiceBaseAdapter tutorial

sukidev edited this page Mar 6, 2014 · 9 revisions

The previous tutorial showed how to create a very simple list using MultiChoiceArrayAdapter. For more complex needs, you'll need the extra flexibility provided by MultiChoiceBaseAdapter. This tutorial will teach you how to use this class to create a list where items have two text fields and a checkbox.

Create a new Android project using Maven or your IDE of choice, and add references to the ActionBarSherlock and MultiChoiceAdapter library projects.

Add a new activity called MainActivity, and make it derive from SherlockActivity. Edit the XML layout for this activity so that it looks like this:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@android:id/list" />

Now you need an adapter to provide the ListView with items to display. Each item will be an instance of the following class:

public class Building {
    public String name;
    public String height;

    public Building(String name, String height) {
        this.name = name;
        this.height = height;
    }
}

The layout for the items will be the following:

<com.manuelpeinado.multichoiceadapter.CheckableRelativeLayout   
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:baselineAligned="false"
    android:minHeight="64dp"
    android:orientation="horizontal"
    android:paddingLeft="6dp"
    android:paddingRight="6dp" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:layout_toLeftOf="@android:id/checkbox"
        android:orientation="vertical" >

        <TextView
            android:id="@android:id/text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium" />

        <TextView
            android:id="@android:id/text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceSmall" />
    </LinearLayout>

    <CheckBox
        android:id="@android:id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:focusable="false" />

</com.manuelpeinado.multichoiceadapter.CheckableRelativeLayout>

Note that you have to use CheckableRelativeLayout, which is a version of RelativeLayout that implements the Checkable interface and has a separate background for the checked state.

Now you have all the pieces you need to implement the adapter:

public class BuildingsAdapter extends MultiChoiceBaseAdapter {
    private List<Building> items;

    public BuildingsAdapter(Bundle savedInstanceState, List<Building> items) {
        super(savedInstanceState);
        this.items = items;
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.my_action_mode, menu);
        return true;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        if (item.getItemId() == R.id.menu_share) {
            Toast.makeText(getContext(), "Share", Toast.LENGTH_SHORT).show();
            return true;
        }
        return false;
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public Building getItem(int position) {
        return items.get(position);
    }

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

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
    }

    @Override
    protected View getViewImpl(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            int layout = R.layout.list_item;
            LayoutInflater inflater = LayoutInflater.from(getContext());
            convertView = inflater.inflate(layout, parent, false);
        }
        ViewGroup group = (ViewGroup)convertView;
        Building building = getItem(position);
        ((TextView)group.findViewById(android.R.id.text1)).setText(building.name);
        ((TextView)group.findViewById(android.R.id.text2)).setText(building.height);
        return group;
    }
}

Now that you have your adapter class ready you can bind it to the ListView. At the end of MainActivity's onCreate method:

ListView listView = (ListView)findViewById(android.R.id.list);
List<Building> items = Arrays.asList(new Building("One World Trade Center", "541 m"), new Building("Empire State Building", "443 m"), new Building("Bank of America Building", "319 m"), new Building("Chrysler Building", "319 m");
adapter = new BuildingsAdapter(savedInstanceState, this, items);
adapter.setAdapterView(listView);
adapter.setOnItemClickListener(new OnItemClickListener() {
 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
        Toast.makeText(this, "Item click: " + adapter.getItem(position), Toast.LENGTH_SHORT).show();
    }
});

Finally you shouldn't forget to persist your adapter's state:

@Override
protected void onSaveInstanceState(Bundle outState) {
    adapter.save(outState);
}
Clone this wiki locally