次の方法で共有


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 つのメソッドが ListActivityOnListItemClick の間に実装されている必要があります。

SimpleListItem のスクリーンショット

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 オブジェクトを作成するメモリの無駄になります。 この状況を回避するために、行が画面から消えたときに、そのビューは再利用のためにキューに配置されます。 ユーザーがスクロールすると、ListViewGetView を呼び出して、表示する新しいビューを要求します。使用可能な場合は、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 メソッドがないため、NewViewBindView の 2 つの異なるメソッドが必要であり、GetView の役割を 2 つのメソッドに分離することで強制的に行が再利用されます。 ドキュメントの後半部分に CursorAdapter の例があります。

高速スクロールの有効化

高速スクロールは、リストの一部に直接アクセスするためのスクロール バーとして機能する追加の "ハンドル" を提供することで、ユーザーが長いリストをスクロールするのに役立ちます。 このスクリーンショットは、高速スクロール ハンドルを示しています。

スクロール ハンドルを使用した高速スクロールのスクリーンショット

高速スクロール ハンドルの表示は、FastScrollEnabled プロパティを true に設定するのと同じくらい簡単です。

ListView.FastScrollEnabled = true;

セクション インデックスの追加

セクション インデックスは、ユーザーが長いリストを高速スクロールしているときに、どの 'セクション' までスクロールしたかを示します。 セクション インデックスを表示するには、アダプター サブクラスで ISectionIndexer インターフェイスを実装し、表示される行に応じてインデックス テキストを提供する必要があります。

H で始まる上のセクションに表示されている H のスクリーンショット

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" をマップする必要があります。