我最终采用的解决方案是分段列表适配器,用于在我的布局中填充单个 ListView 控件。分段适配器使 ListView 与 Preferences List 非常相似。但是分段适配器更加通用,因为您可以自定义分段分隔项,并包含多个列表项布局。假设您已经了解 Mono for Android 的基础知识,下面将详细介绍如何实现这一点。
首先,您需要一个 Section 对象,它将描述每个单独的列表部分。
public class ListSection
{
private String _caption;
private String _columnHeader1, _columnHeader2, _columnHeader3;
private BaseAdapter _adapter;
public ListSection(String caption, String columnHeader1, String columnHeader2, String columnHeader3, BaseAdapter adapter)
{
_caption = caption;
_columnHeader1 = columnHeader1;
_columnHeader2 = columnHeader2;
_columnHeader3 = columnHeader3;
_adapter = adapter;
}
public String Caption { get { return _caption; } set { _caption = value; } }
public String ColumnHeader1 { get { return _columnHeader1; } set { _columnHeader1 = value; } }
public String ColumnHeader2 { get { return _columnHeader2; } set { _columnHeader2 = value; } }
public String ColumnHeader3 { get { return _columnHeader3; } set { _columnHeader3 = value; } }
public BaseAdapter Adapter { get { return _adapter; } set { _adapter = value; } }
}
该对象存储列表中每个部分的所有信息、作为该部分标题的标题以及我希望列表具有的 3 列中的每一列的列标题。此外,我们存储了一个唯一的列表适配器,它将为列表的这一部分提供视图。这允许您为每个部分提供不同的适配器。如果需要,您可以扩展此部分对象以进一步描述分隔部分,从而为您提供更大的灵活性,并有机会更改每个部分的基本结构。
接下来,您需要一个 XML 模板来描述列表的分隔符。由于我的每个部分都将具有相同的基本结构,因此我可以每次都重复使用相同的模板,而不会使它进一步复杂化。
<?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="wrap_content">
<TextView
android:id="@+id/caption"
android:layout_marginTop="10px"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/listSeparatorTextViewStyle">
<TextView
android:id="@+id/columnHeader1"
android:layout_marginLeft="10px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="100px"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/columnHeader2"
android:layout_marginLeft="10px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="100px"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/columnHeader3"
android:layout_marginLeft="10px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="100px"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>
你会注意到,Inner LinearLayout 控件我给了一个 style="?android:attr/listSeparatorTextViewStyle" 标签。这告诉android给那个视图一个底部边框。如果你只想要一个简单的 TextView 分隔符,你可以这样做,并给它同样的样式标签。
现在我的 ListAdapter 基本上都是一样的,它们只是扩展了一个不同的数据对象。这三个都填充了 3 列,所有这些列都适合它们的数据在相同大小的列中,它们只是不同的逻辑对象。每个适配器都是一个 BaseAdapter 扩展,它使用 3 列数据填充视图。我在这里假设您知道如何创建 BaseAdapter 的标准扩展。我将展示如何创建 ListSectionAdapter。
public class ListSectionAdapter : BaseAdapter<ListSection>
{
private const int TYPE_SECTION_HEADER = 0;
private Context _context;
private List<ListSection> _sections;
private LayoutInflater _inflater;
public ListSectionAdapter(Context context)
{
_context = context;
_inflater = Inflater.From(_context);
_sections = new List<ListSection>();
}
public List<ListSection> Sections { get { return _sections; } set { _sections = value; } }
public override int Count
{
get
{
int count = 0;
foreach(ListSection s in _sections) count += s.Adapter.Count + 1;
return count;
}
}
public override int ViewTypeCount
{
get
{
int viewTypeCount = 1;
foreach(ListSection s in _sections) viewTypeCount += s.Adapter.ViewTypeCount;
return viewTypeCount;
}
}
public override ListSection this[int index] { get { return _sections[index]; } }
public override bool AreAllItemsEnable() { return false; }
public override int GetItemViewType(int position)
{
int typeOffset = TYPE_SECTION_HEADER + 1;
foreach(ListSection s in _sections)
{
if(position == 0) return TYPE_SECTION_HEADER;
int size = s.Adapter.Count + 1;
if(position < size) return (typeOffset + s.Adapter.GetItemViewType(position - 1));
position -= size;
typeOffset += s.Adapter.ViewTypeCount;
}
return -1;
}
public override long GetItemId(int position) { return position; }
public void AddSection(String caption, String columnHeader1, String columnHeader2, String columnHeader3, BaseAdapter adapter)
{
_sections.Add(new ListSection(caption, columnHeader1, columnHeader2, columnHeader3, adapter);
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
foreach(ListSection s in _sections)
{
if(position == 0)
{
if(view == null || !(view is LinearLayout)) view = _inflater.Inflate(Resource.Layout.SectionSeparator, parent, false);
TextView caption = view.FindViewById<TextView>(Resource.Id.caption);
caption.Text = s.Caption;
TextView columnHeader1 = view.FindViewById<TextView>(Resource.Id.columnHeader1);
columnHeader1.Text = s.ColumnHeader1;
TextView columnHeader2 = view.FindViewById<TextView>(Resource.Id.columnHeader2);
columnHeader2.Text = s.ColumnHeader2;
TextView columnHeader3 = view.FindViewById<TextView>(Resource.Id.columnHeader3);
columnHeader3.Text = s.ColumnHeader3;
return view;
}
int size = s.Adapter.Count + 1;
if(position < size) return s.Adapter.GetView(position - 1, convertView, parent);
position -= size;
}
return null;
}
public override Java.Lang.Object GetItem(int position)
{
foreach(ListSection s in _sections)
{
if(position == 0) return null;
int size = s.Adapter.Count + 1;
if(position < size) return s.Adapter.GetItem(position);
position -= size;
}
return null;
}
}
现在您所要做的就是在您的代码中,当您填充包含 ListView 的 Activity 或 Layout 时,创建单独的适配器,然后创建分段适配器并为您想要的每个单独的列表类型添加一个部分。
ListAdapterType1 adapter1 = new ListAdapterType1();
ListAdapterType2 adapter2 = new ListAdapterType2();
ListAdapterType3 adapter3 = new ListAdapterType3();
ListSectionAdapter sectionAdapter = new ListSectionAdapter(this);
sectionAdapter.AddSection("Section 1", "Column 1", "Column 2", "Column 3", adapter1);
sectionAdapter.AddSection("Section 2", "Column 1", "Column 2", "Column 3", adapter2);
sectionAdapter.AddSection("Section 3", "Column 1", "Column 2", "Column 3", adapter3);
ListView myList = FindViewById<ListView>(Resource.Id.MyList);
myList.SetAdapter(sectionAdapter);
对于 ItemClick 事件,可能有更好的方法来做到这一点,但我使用了以下方法,它比较了分段列表的 GetItem(int) 方法返回的 Object Types 的 ToString,我们已将其扩展为 return基本列表适配器对象类型。
private void MyList_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
ListSectionAdapter adapter = (sender as ListView).Adapter as ListSectionAdapter;
if(adapter.GetItem(e.Position).ToString() == typeof(ObjectA).ToString())
{
// respond accordingly to object type A being clicked
}
// so on and so forth on each different object type contained in the sectioned list
}
My click event just populates and opens a new Layout describing the item clicked. The differentiation of object type is necessary because I use a different layout based on the object type clicked, because static information on the layout differs per object type.
Hope this helps. I gleaned this example from Wrox's book, Professional Android Programming with Mono C#/.Net, and modified it to meet my needs, hopefully you can see how it works so you can modify it to meet your own needs.