Android – Sectioned Headers in ListViews
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
public class ListSample extends Activity
{
public final static String ITEM_TITLE = "title";
public final static String ITEM_CAPTION = "caption";
// SectionHeaders
private final static String[] days = new String[]{"Mon", "Tue", "Wed", "Thur", "Fri"};
// Section Contents
private final static String[] notes = new String[]{"Ate Breakfast", "Ran a Marathan ...yah really", "Slept all day"};
// MENU - ListView
private ListView addJournalEntryItem;
// Adapter for ListView Contents
private SeparatedListAdapter adapter;
// ListView Contents
private ListView journalListView;
public Map<String, ?> createItem(String title, String caption)
{
Map<String, String> item = new HashMap<String, String>();
item.put(ITEM_TITLE, title);
item.put(ITEM_CAPTION, caption);
return item;
}
@Override
public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
// Sets the View Layer
setContentView(R.layout.main);
// Interactive Tools
final ArrayAdapter<String> journalEntryAdapter = new ArrayAdapter<String>(this, R.layout.add_journalentry_menuitem, new String[]{"Add Journal Entry"});
// AddJournalEntryItem
addJournalEntryItem = (ListView) this.findViewById(R.id.add_journalentry_menuitem);
addJournalEntryItem.setAdapter(journalEntryAdapter);
addJournalEntryItem.setOnItemClickListener(new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long duration)
{
String item = journalEntryAdapter.getItem(position);
Toast.makeText(getApplicationContext(), item, Toast.LENGTH_SHORT).show();
}
});
// Create the ListView Adapter
adapter = new SeparatedListAdapter(this);
ArrayAdapter<String> listadapter = new ArrayAdapter<String>(this, R.layout.list_item, notes);
// Add Sections
for (int i = 0; i < days.length; i++)
{
adapter.addSection(days[i], listadapter);
}
// Get a reference to the ListView holder
journalListView = (ListView) this.findViewById(R.id.list_journal);
// Set the adapter on the ListView holder
journalListView.setAdapter(adapter);
// Listen for Click events
journalListView.setOnItemClickListener(new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long duration)
{
String item = (String) adapter.getItem(position);
Toast.makeText(getApplicationContext(), item, Toast.LENGTH_SHORT).show();
}
});
}
}
import java.util.LinkedHashMap;
import java.util.Map;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
public class SeparatedListAdapter extends BaseAdapter
{
public final Map<String, Adapter> sections = new LinkedHashMap<String, Adapter>();
public final ArrayAdapter<String> headers;
public final static int TYPE_SECTION_HEADER = 0;
public SeparatedListAdapter(Context context)
{
headers = new ArrayAdapter<String>(context, R.layout.list_header);
}
public void addSection(String section, Adapter adapter)
{
this.headers.add(section);
this.sections.put(section, adapter);
}
public Object getItem(int position)
{
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return section;
if (position < size) return adapter.getItem(position - 1);
// otherwise jump into next section
position -= size;
}
return null;
}
public int getCount()
{
// total together all sections, plus one for each section header
int total = 0;
for (Adapter adapter : this.sections.values())
total += adapter.getCount() + 1;
return total;
}
@Override
public int getViewTypeCount()
{
// assume that headers count as one, then total all sections
int total = 1;
for (Adapter adapter : this.sections.values())
total += adapter.getViewTypeCount();
return total;
}
@Override
public int getItemViewType(int position)
{
int type = 1;
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return TYPE_SECTION_HEADER;
if (position < size) return type + adapter.getItemViewType(position - 1);
// otherwise jump into next section
position -= size;
type += adapter.getViewTypeCount();
}
return -1;
}
public boolean areAllItemsSelectable()
{
return false;
}
@Override
public boolean isEnabled(int position)
{
return (getItemViewType(position) != TYPE_SECTION_HEADER);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
int sectionnum = 0;
for (Object section : this.sections.keySet())
{
Adapter adapter = sections.get(section);
int size = adapter.getCount() + 1;
// check if position inside this section
if (position == 0) return headers.getView(sectionnum, convertView, parent);
if (position < size) return adapter.getView(position - 1, convertView, parent);
// otherwise jump into next section
position -= size;
sectionnum++;
}
return null;
}
@Override
public long getItemId(int position)
{
return position;
}
}
<?xml version="1.0" encoding="utf-8"?>
<!-- list_item.xml -->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_item_title"
android:gravity="right"
android:drawableRight="@drawable/ic_menu_add"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="0dip"
android:paddingBottom="0dip"
android:paddingLeft="10dip"
android:textAppearance="?android:attr/textAppearanceLarge" />
<?xml version="1.0" encoding="utf-8"?>
<!-- list_complex.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="10dip"
android:paddingBottom="10dip"
android:paddingLeft="15dip">
<TextView
android:id="@+id/list_complex_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<TextView
android:id="@+id/list_complex_caption"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_header_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="2dip"
android:paddingBottom="2dip"
android:paddingLeft="5dip"
style="?android:attr/listSeparatorTextViewStyle" />
<?xml version="1.0" encoding="utf-8"?>
<!-- list_item.xml -->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_item_title"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="10dip"
android:paddingBottom="10dip"
android:paddingLeft="15dip"
android:textAppearance="?android:attr/textAppearanceLarge" />
<?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">
<ListView
android:id="@+id/add_journalentry_menuitem"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ListView
android:id="@+id/list_journal"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>