Separating Lists with Headers

Separating Lists with Headers in Android 0.9

Monday August 18, 2008 at 4:42 PM

Earlier today thelatest Android 0.9 SDK was released, and it’s packed full of wonderful changes. As you play around, you might see ListViews split into sections using separating headers. (Example shown on the right is the browser settings list.)

There isn’t an easy way of creating these separated lists, so I’ve put together SeparatedListAdapter which does it quickly. To summarize, we’re creating a new BaseAdapter that can contain several other Adapters, each with their own section headers.

First let’s create some simple XML layouts to be used for our lists: first the header view, then two item views that we’ll use later for the individual lists. (Thanks to Romain Guy for helping me find existing styles to keep these XML layouts nice and tidy.)

  1. <TextView
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@+id/list_header_title"
  4. android:layout_width="fill_parent"
  5. android:layout_height="wrap_content"
  6. android:paddingTop="2dip"
  7. android:paddingBottom="2dip"
  8. android:paddingLeft="5dip"
  9. style="?android:attr/listSeparatorTextViewStyle"/>
  10. <TextView
  11. xmlns:android="http://schemas.android.com/apk/res/android"
  12. android:id="@+id/list_item_title"
  13. android:layout_width="fill_parent"
  14. android:layout_height="fill_parent"
  15. android:paddingTop="10dip"
  16. android:paddingBottom="10dip"
  17. android:paddingLeft="15dip"
  18. android:textAppearance="?android:attr/textAppearanceLarge"
  19. />
  20. <LinearLayout
  21. xmlns:android="http://schemas.android.com/apk/res/android"
  22. android:layout_width="fill_parent"
  23. android:layout_height="wrap_content"
  24. android:orientation="vertical"
  25. android:paddingTop="10dip"
  26. android:paddingBottom="10dip"
  27. android:paddingLeft="15dip"
  28. >
  29. <TextView
  30. android:id="@+id/list_complex_title"
  31. android:layout_width="fill_parent"
  32. android:layout_height="wrap_content"
  33. android:textAppearance="?android:attr/textAppearanceLarge"
  34. />
  35. <TextView
  36. android:id="@+id/list_complex_caption"
  37. android:layout_width="fill_parent"
  38. android:layout_height="wrap_content"
  39. android:textAppearance="?android:attr/textAppearanceSmall"
  40. />
  41. LinearLayout>

Now let’s create the actual SeparatedListAdapter class which provides a single interface to multiple sections of other Adapters. After using addSection() to construct the child sections, you can easily use ListView.setAdapter() to present the now-separated list to users.

As for the Adapter internals, to correctly find the selected item among the child Adapters, we walk through subtracting from the original position until we find either a header (position = 0) or item in the current child Adapter (position <>

Here’s the source for SeparatedListAdapter:

  1. publicclassSeparatedListAdapterextendsBaseAdapter {
  2. publicfinalMapsections =newLinkedHashMap();
  3. publicfinalArrayAdapterheaders;
  4. publicfinalstaticintTYPE_SECTION_HEADER =0;
  5. publicSeparatedListAdapter(Context context) {
  6. headers =newArrayAdapter(context, R.layout.list_header);
  7. }
  8. publicvoidaddSection(String section, Adapter adapter) {
  9. this.headers.add(section);
  10. this.sections.put(section, adapter);
  11. }
  12. publicObject getItem(intposition) {
  13. for(Object section :this.sections.keySet()) {
  14. Adapter adapter = sections.get(section);
  15. intsize = adapter.getCount() +1;
  16. // check if position inside this section
  17. if(position ==0)returnsection;
  18. if(position <>returnadapter.getItem(position -1);
  19. // otherwise jump into next section
  20. position -= size;
  21. }
  22. returnnull;
  23. }
  24. publicintgetCount() {
  25. // total together all sections, plus one for each section header
  26. inttotal =0;
  27. for(Adapter adapter :this.sections.values())
  28. total += adapter.getCount() +1;
  29. returntotal;
  30. }
  31. publicintgetViewTypeCount() {
  32. // assume that headers count as one, then total all sections
  33. inttotal =1;
  34. for(Adapter adapter :this.sections.values())
  35. total += adapter.getViewTypeCount();
  36. returntotal;
  37. }
  38. publicintgetItemViewType(intposition) {
  39. inttype =1;
  40. for(Object section :this.sections.keySet()) {
  41. Adapter adapter = sections.get(section);
  42. intsize = adapter.getCount() +1;
  43. // check if position inside this section
  44. if(position ==0)returnTYPE_SECTION_HEADER;
  45. if(position <>returntype + adapter.getItemViewType(position -1);
  46. // otherwise jump into next section
  47. position -= size;
  48. type += adapter.getViewTypeCount();
  49. }
  50. return-1;
  51. }
  52. publicbooleanareAllItemsSelectable() {
  53. returnfalse;
  54. }
  55. publicbooleanisEnabled(intposition) {
  56. return(getItemViewType(position) != TYPE_SECTION_HEADER);
  57. }
  58. @Override
  59. publicView getView(intposition, View convertView, ViewGroup parent) {
  60. intsectionnum =0;
  61. for(Object section :this.sections.keySet()) {
  62. Adapter adapter = sections.get(section);
  63. intsize = adapter.getCount() +1;
  64. // check if position inside this section
  65. if(position ==0)returnheaders.getView(sectionnum, convertView, parent);
  66. if(position <>returnadapter.getView(position -1, convertView, parent);
  67. // otherwise jump into next section
  68. position -= size;
  69. sectionnum++;
  70. }
  71. returnnull;
  72. }
  73. @Override
  74. publiclonggetItemId(intposition) {
  75. returnposition;
  76. }
  77. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值