私が最終的に使用したソリューションは、レイアウトに単一の ListView コントロールを設定するために使用される Sectioned List Adapter でした。Sectioned Adapter により、ListView はプリファレンス リストに非常に似ています。ただし、Sectioned Adapter は、セクション区切り項目をカスタマイズして、複数のリスト項目レイアウトを含めることができるため、より用途が広いです。Mono for Android の基本を既に理解していることを前提として、これを実現する方法の内訳を次に示します。
まず、個々のリスト セクションを記述するセクション オブジェクトが必要です。
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>
お気づきでしょう、私が style="?android:attr/listSeparatorTextViewStyle" タグを付けた Inner LinearLayout コントロール。これは、そのビューに下の境界線を与えるように Android に指示します。単純な TextView セパレーターが必要な場合は、これを実行して、同じスタイル タグを付けるだけです。
現在、私の ListAdapter はすべて基本的に同じで、異なるデータ オブジェクトを拡張するだけです。3 つすべてが 3 つの列にデータを入力し、そのすべてが同じサイズの列にデータを収めます。それらは単に異なる論理オブジェクトです。各アダプターは、ビューに 3 列のデータを設定する BaseAdapter 拡張機能です。ここでは、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 を含むアクティビティまたはレイアウトにデータを入力するときにコードを作成し、個別のアダプターを作成してから、セクション化されたアダプターを作成し、必要な個別のリスト タイプごとにセクションを追加するだけです。
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) メソッドから返されたオブジェクト タイプの ToString を比較します。ベース リスト アダプタ オブジェクト タイプ。
private void MyList_ItemClick(オブジェクト送信者、AdapterView.ItemClickEventArgs e) { ListSectionAdapter アダプタ = (ListView としての送信者).Adapter として ListSectionAdapter; if(adapter.GetItem(e.Position).ToString() == typeof(ObjectA).ToString()) { // クリックされたオブジェクト タイプ A に応じて応答する } // 含まれる異なるオブジェクト タイプごとに同様の処理を行うセクション化されたリストで }
私のクリックイベントは、クリックされたアイテムを説明する新しいレイアウトを作成して開きます。レイアウト上の静的情報はオブジェクト タイプごとに異なるため、クリックされたオブジェクト タイプに基づいて異なるレイアウトを使用するため、オブジェクト タイプの区別が必要です。
お役に立てれば。この例は、Wrox の著書『Professional Android Programming with Mono C#/.Net』から収集し、私のニーズに合わせて変更しました。うまくいけば、どのように機能するかを確認して、自分のニーズに合わせて変更できることを願っています。