Xamarin.Android を使った ListView の外観のカスタマイズ
ListView の外観は、表示される行のレイアウトによって決まります。 ListView
の外観を変更するには、異なる行レイアウトを使います。
組み込みの行ビュー
Android.Resource.Layout を使って参照できる組み込みビューが 12 個あります。
TestListItem - 最小限の書式設定を備えた 1 行のテキスト。
SimpleListItem1 - 1 行のテキスト。
SimpleListItem2 - 2 行のテキスト。
SimpleSelectableListItem - 単一または複数の項目選択をサポートする 1 行のテキスト (API レベル 11 で追加)。
SimpleListItemActivated1 - SimpleListItem1 と似ていますが、背景色は行が選択されたことを示します (API レベル 11 で追加)。
SimpleListItemActivated2 - SimpleListItem2 と似ていますが、背景色は行が選択されたことを示します (API レベル 11 で追加)。
SimpleListItemChecked - 選択を示すチェック マークを表示します。
SimpleListItemMultipleChoice - 複数選択の選択項目を示すチェック ボックスを表示します。
SimpleListItemSingleChoice - 相互に排他的な選択項目を示すラジオ ボタンを表示します。
TwoLineListItem - 2 行のテキスト。
ActivityListItem - 画像を含む 1 行のテキスト。
SimpleExpandableListItem - カテゴリごとに行をグループ化し、各グループを展開または折りたたむことができます。
組み込みの各行ビューには、それに関連付けられた組み込みスタイルがあります。 以下のスクリーンショットは、各ビューがどのように表示されるかを示しています。
BuiltInViews/HomeScreenAdapter.cs サンプル ファイル (BuiltInViews ソリューション内) には、展開できないリスト項目画面を生成するコードが含まれています。 ビューは、次のように GetView
メソッドで設定されます。
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
その後、Android.Resource.Id
に含まれる標準コントロール識別子 Text1
、Text2
、Icon
を参照してビューのプロパティを設定できます (ビューに含まれていないプロパティは設定しないでください。例外がスローされます)。
view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = item.Heading;
view.FindViewById<TextView>(Android.Resource.Id.Text2).Text = item.SubHeading;
view.FindViewById<ImageView>(Android.Resource.Id.Icon).SetImageResource(item.ImageResourceId); // only use with ActivityListItem
BuiltInExpandableViews/ExpandableScreenAdapter.cs サンプル ファイル (BuiltInViews ソリューション内) には、SimpleExpandableListItem 画面を生成するコードが含まれています。 グループ ビューは、次のように GetGroupView
メソッドで設定されます。
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleExpandableListItem1, null);
子ビューは、次のように GetChildView
メソッドで設定されます。
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleExpandableListItem2, null);
グループ ビューと子ビューのプロパティは、上に示したように標準の Text1
および Text2
コントロール識別子を参照して設定できます。 SimpleExpandableListItem のスクリーンショット (上に表示) は、1 行のグループ ビュー (SimpleExpandableListItem1) と 2 行の子ビュー (SimpleExpandableListItem2) の例を示しています。 または、グループ ビューを 2 行 (SimpleExpandableListItem2) に構成し、子ビューを 1 行 (SimpleExpandableListItem1) に構成することも、グループ ビューと子ビューの両方に同じ行数を含めることもできます。
アクセサリ
行には、選択状態を示すためにビューの右側にアクセサリを追加できます。
SimpleListItemChecked - チェック マークをインジケーターとして使って単一選択のリストを作成します。
SimpleListItemSingleChoice - 選択が 1 つだけ可能なラジオ ボタン型のリストを作成します。
SimpleListItemMultipleChoice - 複数選択が可能なチェックボックス型のリストを作成します。
上記のアクセサリは、次の画面にそれぞれの順序で示されています。
これらのアクセサリのいずれかを表示するには、必要なレイアウト リソース ID をアダプターに渡し、それから必要な行の選択状態を手動で設定します。 このコード行は、次のレイアウトのいずれかを使って Adapter
を作成して割り当てる方法を示しています。
ListAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleListItemChecked, items);
ListView
自体は、表示されているアクセサリに関係なく、さまざまな選択モードをサポートしています。 混乱を避けるため、SingleChoice
アクセサリでは Single
選択モードを使い、MultipleChoice
スタイルでは Checked
または Multiple
モードを使います。 選択モードは、ListView
の ChoiceMode
プロパティによって制御されます。
API レベルの処理
Xamarin.Android の以前のバージョンでは、列挙型が整数プロパティとして実装されていました。 最新バージョンでは、適切な .NET 列挙型が導入されており、潜在的なオプションを簡単に検出できるようになりました。
対象とする API レベルに応じて、ChoiceMode
は整数または列挙のいずれかになります。 サンプル ファイル AccessoryViews/HomeScreen.cs には、Gingerbread API をターゲットにする場合にコメント アウトされたブロックがあります。
// For targeting Gingerbread the ChoiceMode is an int, otherwise it is an
// enumeration.
lv.ChoiceMode = Android.Widget.ChoiceMode.Single; // 1
//lv.ChoiceMode = Android.Widget.ChoiceMode.Multiple; // 2
//lv.ChoiceMode = Android.Widget.ChoiceMode.None; // 0
// Use this block if targeting Gingerbread or lower
/*
lv.ChoiceMode = 1; // Single
//lv.ChoiceMode = 0; // none
//lv.ChoiceMode = 2; // Multiple
//lv.ChoiceMode = 3; // MultipleModal
*/
プログラムによる項目の選択
どの項目を "選択" するかを手動で設定するには、SetItemChecked
メソッドを使います (複数選ぶ場合は複数回呼び出すことができます)。
// Set the initially checked row ("Fruits")
lv.SetItemChecked(1, true);
コードでは、複数選択とは異なる方法で単一選択を検出する必要もあります。 Single
モードでどの行が選ばれているかを確認するには、整数プロパティ CheckedItemPosition
を使います。
FindViewById<ListView>(Android.Resource.Id.List).CheckedItemPosition
Multiple
モードで選択されている行を確認するには、CheckedItemPositions
SparseBooleanArray
をループする必要があります。 スパース配列は、値が変更されたエントリのみを含むディクショナリのようなものであるため、次のコード スニペットに示すように、リスト内で何が選ばれているかを知るには、配列全体を走査して true
値を探す必要があります。
var sparseArray = FindViewById<ListView>(Android.Resource.Id.List).CheckedItemPositions;
for (var i = 0; i < sparseArray.Size(); i++ )
{
Console.Write(sparseArray.KeyAt(i) + "=" + sparseArray.ValueAt(i) + ",");
}
Console.WriteLine();
カスタム行レイアウトの作成
4 つの組み込みの行ビューは非常に単純です。 より複雑なレイアウト (メール、ツイート、連絡先情報の一覧など) を表示するには、カスタム ビューが必要です。 カスタム ビューは、通常、Resources/Layout ディレクトリで AXML ファイルとして宣言され、カスタム アダプターによってリソース ID を使って読み込まれます。 ビューには、カスタムの色、フォント、レイアウトを備えた任意の数の表示クラス (TextView、ImageView、その他のコントロールなど) を含めることができます。
この例は、さまざまな点で前の例とは異なります。
ListActivity
ではなく、Activity
から継承します。 任意のListView
の行をカスタマイズできますが、他のコントロール (見出し、ボタン、その他のユーザー インターフェイス要素など) をActivity
レイアウトに含めることもできます。 この例では、説明のためにListView
の上に見出しを追加します。画面には AXML レイアウト ファイルが必要です。前の例では、
ListActivity
にはレイアウト ファイルは必要ありません。 この AXML には、ListView
コントロール宣言が含まれています。各行をレンダリングするには AXML レイアウト ファイルが必要です。 この AXML ファイルには、カスタム フォントと色の設定を備えたテキストとイメージのコントロールが含まれています。
省略可能なカスタム セレクター XML ファイルを使って、行が選ばれたときの外観を設定します。
Adapter
実装では、GetView
オーバーライドからカスタム レイアウトが返されます。ItemClick
は別の方法で宣言する必要があります (イベント ハンドラーは、ListActivity
のOnListItemClick
をオーバーライドするのではなく、ListView.ItemClick
にアタッチされます)。
これらの変更については、以下で詳しく説明します。まず、アクティビティのビューとカスタム行ビューの作成から始めて、それらをレンダリングするためのアダプターとアクティビティへの変更について説明します。
ListView のアクティビティ レイアウトへの追加
HomeScreen
は ListActivity
から継承されなくなり、既定のビューがないため、HomeScreen のビュー用にレイアウト AXML ファイルを作成する必要があります。 この例では、ビューにはデータを表示するための見出し (TextView
を使用) と ListView
が含まれます。 レイアウトは、次に示す Resources/Layout/HomeScreen.axml ファイルで定義されます。
<?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="fill_parent">
<TextView android:id="@+id/Heading"
android:text="Vegetable Groups"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:textSize="30dp"
android:textColor="#FF267F00"
android:textStyle="bold"
android:padding="5dp"
/>
<ListView android:id="@+id/List"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#FFDAFF7F"
/>
</LinearLayout>
(ListActivity
ではなく) カスタム レイアウトで Activity
を使う利点は、この例の見出し TextView
など、付加的なコントロールを画面に追加できることです。
カスタム行レイアウトの作成
リスト ビューに表示される各行のカスタム レイアウトを含めるには、別の AXML レイアウト ファイルが必要です。 この例では、行の背景が緑色、テキストが茶色、画像が右揃えになります。 このレイアウトを宣言する Android XML マークアップは、Resources/Layout/CustomView.axml で説明されています。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FFDAFF7F"
android:padding="8dp">
<LinearLayout android:id="@+id/Text"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<TextView
android:id="@+id/Text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic"
/>
<TextView
android:id="@+id/Text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#FF267F00"
android:paddingLeft="100dip"
/>
</LinearLayout>
<ImageView
android:id="@+id/Image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="5dp"
android:src="@drawable/icon"
android:layout_alignParentRight="true" />
</RelativeLayout >
カスタム行レイアウトにはさまざまなコントロールを含めることができますが、スクロールのパフォーマンスは、複雑なデザインや画像の使用 (特にネットワーク経由で読み込む必要がある場合) によって影響を受ける可能性があります。 スクロールのパフォーマンスの問題への対処方法の詳細については、Google の記事を参照してください。
カスタム行ビューの参照
カスタム アダプター実装の例は HomeScreenAdapter.cs
にあります。 主要なメソッドは GetView
で、リソース ID Resource.Layout.CustomView
を使ってカスタム AXML を読み込んでから、ビュー内の各コントロールのプロパティを、それを返す前に設定します。 完全なアダプター クラスを次に示します。
public class HomeScreenAdapter : BaseAdapter<TableItem> {
List<TableItem> items;
Activity context;
public HomeScreenAdapter(Activity context, List<TableItem> items)
: base()
{
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override TableItem this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
View view = convertView;
if (view == null) // no view to re-use, create new
view = context.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
view.FindViewById<TextView>(Resource.Id.Text1).Text = item.Heading;
view.FindViewById<TextView>(Resource.Id.Text2).Text = item.SubHeading;
view.FindViewById<ImageView>(Resource.Id.Image).SetImageResource(item.ImageResourceId);
return view;
}
}
アクティビティでのカスタム ListView の参照
HomeScreen
クラスは Activity
を継承するようになったため、クラス内で ListView
フィールドが宣言され、AXML で宣言されたコントロールへの参照が保持されます。
ListView listView;
次に、クラスは、SetContentView
メソッドを使ってアクティビティのカスタム レイアウト AXML を読み込む必要があります。 その後、レイアウト内で ListView
コントロールを見つけて、アダプターを作成して割り当て、クリック ハンドラーを割り当てます。 OnCreate メソッドのコードを次に示します。
SetContentView(Resource.Layout.HomeScreen); // loads the HomeScreen.axml as this activity's view
listView = FindViewById<ListView>(Resource.Id.List); // get reference to the ListView in the layout
// populate the listview with data
listView.Adapter = new HomeScreenAdapter(this, tableItems);
listView.ItemClick += OnListItemClick; // to be defined
最後に、ItemClick
ハンドラーを定義する必要があります。この場合は、Toast
メッセージを表示するだけです。
void OnListItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
var listView = sender as ListView;
var t = tableItems[e.Position];
Android.Widget.Toast.MakeText(this, t.Heading, Android.Widget.ToastLength.Short).Show();
}
結果の画面は次のようになります。
行セレクターの色のカスタマイズ
行がタッチされると、ユーザー フィードバックのためにそれが強調表示されます。 CustomView.axml のようにカスタム ビューが背景色として指定されている場合、選択の強調表示もオーバーライドされます。 CustomView.axml のこのコード行は背景を明るい緑色に設定しますが、これは行がタッチされたときに視覚的なインジケーターがないことも意味します。
android:background="#FFDAFF7F"
強調表示動作を再度有効にし、使われる色をカスタマイズするには、代わりに背景属性をカスタム セレクターに設定します。 セレクターでは、既定の背景色と強調表示の色の両方を宣言します。 ファイル Resources/Drawable/CustomSelector.xml には次の宣言が含まれています。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false"
android:state_selected="false"
android:drawable="@color/cellback" />
<item android:state_pressed="true" >
<shape>
<gradient
android:startColor="#E77A26"
android:endColor="#E77A26"
android:angle="270" />
</shape>
</item>
<item android:state_selected="true"
android:state_pressed="false"
android:drawable="@color/cellback" />
</selector>
カスタム セレクターを参照するには、CustomView.axml の背景属性を次のように変更します。
android:background="@drawable/CustomSelector"
選択した行行と対応する Toast
メッセージは次のようになります。
カスタム レイアウトでのちらつきの防止
Android では、レイアウト情報をキャッシュすることで、ListView
スクロールのパフォーマンスを向上させようとしています。 スクロールするデータのリストが長い場合は、アクティビティの AXML 定義の ListView
宣言で android:cacheColorHint
プロパティを (カスタム行レイアウトの背景と同じ色の値に) 設定する必要もあります。 このヒントを含めないと、ユーザーがカスタム行の背景色を持つリストをスクロールするときに "ちらつき" が発生する可能性があります。