Xamarin.Android ListView へのデータの入力
ListView
に行を追加するには、行をレイアウトに追加し、ListView
呼び出し自体を設定するメソッドを使用して、IListAdapter
を実装する必要があります。 Android には、カスタム レイアウト XML またはコードを定義せずに使用できる組み込みの ListActivity
クラスと ArrayAdapter
クラスがあります。 ListActivity
クラスは、ListView
を自動的に作成し、アダプターを介して表示する行ビューを指定する ListAdapter
プロパティを公開します。
組み込みアダプターは、各行に使用されるパラメーターとしてビュー リソース ID を受け取ります。 Android.Resource.Layout
にあるような組み込みのリソースを使うことができるため、独自のリソースを記述する必要はありません。
ListActivity および ArrayAdapter <文字列>の使用
BasicTable/HomeScreen.cs の例では、これらのクラスを使用して、わずか数行のコードのみで ListView
を表示する方法について説明します。
[Activity(Label = "BasicTable", MainLauncher = true, Icon = "@drawable/icon")]
public class HomeScreen : ListActivity {
string[] items;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
items = new string[] { "Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers" };
ListAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleListItem1, items);
}
}
行クリックの処理
通常、ListView
では、ユーザーが行をタッチして何らかのアクション (曲の再生、連絡先の呼び出し、別の画面の表示など) を実行することもできます。 ユーザーのタッチに応答するには、次に示すように、もう 1 つのメソッドが ListActivity
と OnListItemClick
の間に実装されている必要があります。
protected override void OnListItemClick(ListView l, View v, int position, long id)
{
var t = items[position];
Android.Widget.Toast.MakeText(this, t, Android.Widget.ToastLength.Short).Show();
}
これで、ユーザーが行にタッチすると Toast
アラートが表示されます。
ListAdapter の実装
ArrayAdapter<string>
は、そのシンプルさゆえに素晴らしいものですが、非常に限定的です。 ただし、多くの場合、バインドするのは文字列だけでなく、ビジネス エンティティのコレクションもあります。
たとえば、データが Employee クラスのコレクションで構成されている場合に、リストに各従業員の名前のみ表示する必要があるとします。 表示されるデータを制御する ListView
の動作をカスタマイズするには、次の 4 つの項目をオーバーライドする BaseAdapter
のサブクラスを実装する必要があります。
Count – データ内の行数をコントロールに伝達します。
GetView – 各行のビューを返すために、データが設定されます。 このメソッドには
ListView
のパラメーターがあり、再利用のために既存の未使用の行を渡すことができます。GetItemId – 行の識別子を返します (通常は行番号ですが、任意の長い値を指定できます)。
this[int] インデクサー – 特定の行番号に関連付けられているデータを返します。
BasicTableAdapter/HomeScreenAdapter.cs のコード例は、BaseAdapter
をサブクラス化する方法を示しています。
public class HomeScreenAdapter : BaseAdapter<string> {
string[] items;
Activity context;
public HomeScreenAdapter(Activity context, string[] items) : base() {
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override string this[int position] {
get { return items[position]; }
}
public override int Count {
get { return items.Length; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView; // re-use an existing view, if one is available
if (view == null) // otherwise create a new one
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = items[position];
return view;
}
}
カスタム アダプターの使用
カスタム アダプターの使用は、表示する値の context
および string[]
を渡す、組み込みの ArrayAdapter
と似ています。
ListAdapter = new HomeScreenAdapter(this, items);
この例では同じ行レイアウト (SimpleListItem1
) を使用するため、結果のアプリケーションは前の例と同じように見えます。
行表示の再利用
この例では、6 つの項目しかありません。 画面には 8 行が収まるため、行の再利用は必要ありません。 ただし、数百行や数千行を表示する場合は、一度に 8 行しか画面に収まらない場合に、数百または数千の View
オブジェクトを作成するメモリの無駄になります。 この状況を回避するために、行が画面から消えたときに、そのビューは再利用のためにキューに配置されます。 ユーザーがスクロールすると、ListView
は GetView
を呼び出して、表示する新しいビューを要求します。使用可能な場合は、convertView
パラメーターに未使用のビューを渡します。 この値が null の場合、コードは新しいビュー インスタンスを作成する必要があります。それ以外の場合は、そのオブジェクトのプロパティを再設定して再利用できます。
GetView
メソッドは、このパターンに従って次の行ビューを再利用する必要があります。
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView; // re-use an existing view, if one is supplied
if (view == null) // otherwise create a new one
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
// set view properties to reflect data for the given row
view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = items[position];
// return the view, populated with data, for display
return view;
}
カスタム アダプターの実装では、長いリストを表示するときにメモリが不足しないように、新しいビューを作成する前に、常にconvertView
オブジェクトを再利用する必要があります。
一部のアダプター実装 (CursorAdapter
など) には GetView
メソッドがないため、NewView
と BindView
の 2 つの異なるメソッドが必要であり、GetView
の役割を 2 つのメソッドに分離することで強制的に行が再利用されます。 ドキュメントの後半部分に CursorAdapter
の例があります。
高速スクロールの有効化
高速スクロールは、リストの一部に直接アクセスするためのスクロール バーとして機能する追加の "ハンドル" を提供することで、ユーザーが長いリストをスクロールするのに役立ちます。 このスクリーンショットは、高速スクロール ハンドルを示しています。
高速スクロール ハンドルの表示は、FastScrollEnabled
プロパティを true
に設定するのと同じくらい簡単です。
ListView.FastScrollEnabled = true;
セクション インデックスの追加
セクション インデックスは、ユーザーが長いリストを高速スクロールしているときに、どの 'セクション' までスクロールしたかを示します。 セクション インデックスを表示するには、アダプター サブクラスで ISectionIndexer
インターフェイスを実装し、表示される行に応じてインデックス テキストを提供する必要があります。
ISectionIndexer
を実装するには、アダプターに次の 3 つのメソッドを追加する必要があります。
GetSections – 表示できるセクション インデックス タイトルの完全な一覧が提供されます。 このメソッドには Java オブジェクトの配列が必要であるため、コードは .NET コレクションから
Java.Lang.Object[]
を作成する必要があります。 この例では、リスト内の最初の文字のリストをJava.Lang.String
として返します。GetPositionForSection – 特定のセクション インデックスの最初の行の位置を返します。
GetSectionForPosition – 特定の行に表示されるセクション インデックスを返します。
例の SectionIndex/HomeScreenAdapter.cs
ファイルでは、これらのメソッドと、コンストラクター内のいくつかの追加コードが実装されています。 コンストラクターは、すべての行をループし、タイトルの最初の文字を抽出することによってセクション インデックスを構築します (これを機能させるには、項目が既に並べ替えられている必要があります)。
alphaIndex = new Dictionary<string, int>();
for (int i = 0; i < items.Length; i++) { // loop through items
var key = items[i][0].ToString();
if (!alphaIndex.ContainsKey(key))
alphaIndex.Add(key, i); // add each 'new' letter to the index
}
sections = new string[alphaIndex.Keys.Count];
alphaIndex.Keys.CopyTo(sections, 0); // convert letters list to string[]
// Interface requires a Java.Lang.Object[], so we create one here
sectionsObjects = new Java.Lang.Object[sections.Length];
for (int i = 0; i < sections.Length; i++) {
sectionsObjects[i] = new Java.Lang.String(sections[i]);
}
データ構造を作成すると、ISectionIndexer
メソッドは非常に単純になります。
public Java.Lang.Object[] GetSections()
{
return sectionsObjects;
}
public int GetPositionForSection(int section)
{
return alphaIndexer[sections[section]];
}
public int GetSectionForPosition(int position)
{ // this method isn't called in this example, but code is provided for completeness
int prevSection = 0;
for (int i = 0; i < sections.Length; i++)
{
if (GetPositionForSection(i) > position)
{
break;
}
prevSection = i;
}
return prevSection;
}
セクション インデックスのタイトルは、実際のセクションに 1:1 でマップする必要はありません。 これが、GetPositionForSection
メソッドが存在する理由です。
GetPositionForSection
では、インデックス リストにあるインデックスを、リスト ビューにあるセクションにマップできます。 たとえば、インデックスに "z" があっても、すべての文字にテーブル セクションがあるわけではないため、"z" を 26 にマップするのではなく、25 または 24 にマップするか、セクション インデックス "z" をマップする必要があります。