AndroidはMono c#セグメントリストビューを使用
33824 ワード
複数のListViewコントロールを1つのレイアウトに配置する方法を知りたいのですが、表示時に複数のリスト項目を正しく表現させますか?正しくスクロールするのに問題がありますか?この例では、個々のListViewコントロールを単一のListViewに結合し、各サブセクションが独自のListAdapterを使用するサブセクションに分割する方法を示します.リストビューコントロールが実際にネストされていないことを明らかにする必要があります.リストビューでサブセクションを使用し、各リスト項目を動的に埋め込みます.背景:この例では、AndroidとMono c#コードに詳しいと仮定します.私はWroxの本の中の1つの例に基づいて、専門のAndroidのプログラミングとMonoはAndroidとです.net/c#.この例は本の中の例をいくつか修正した.この方法では、スクロール動作が正常に動作します.ベストプラクティスは、1つのレイアウトに1つ以上のListViewがないことです.これにより、ListViewは各リスト項目をデフォルトで表示し、各ListViewを強制的に個別にスクロールします.これは非常に悩ましい行為であり、所望の行為は各ListViewがそのすべてのリスト項目を表示し、親レイアウト処理がスクロールできるようにすることである.この方法では、この動作を実行できます.また、図書の例を拡張し、ListViewの処理方法を示しました.ItemClickedイベントは、サンプルが複数のリスト・アイテム・タイプを組み合わせ、各リスト・アイテム・タイプがコードを使用して独自のアダプタから芽生えるため、正しいアイテム・タイプを正しく処理します.レイアウトには、定義した各異なるタイプの食べ物をパーティション化するリストビューが表示されます.まず、私たちのデータモデルを定義します.非表示、コードのコピー
public class MeatType
{
private double _pricePerPound;
public MeatType(String name, String description, double pricePerPound)
{
_name = name;
_description = description;
_pricePerPound = pricePerPound;
}
public String Name
{
get { return _name; } set { _name = value; }
}
public String Description
{
get { return _description; } set { _description = value; }
}
public double PricePerPound
{
get { return _pricePerPound; } set { _pricePerPound = value; }
}
}
簡単にするために、私たちは野菜類と果物類を持っています.それらの構造は肉類と同じですが、私はここでそれらをリストしません.それらの構造は同じですから.次に、私たちの食べ物のタイプリスト項目のレイアウトを説明するテンプレートが必要です.各食品タイプについて個別のListAdapterを記述するが、この例ではアダプタは同じリスト項目テンプレートを使用することができるため、FoodTypeListItem.xmlが1つしか必要ない.このテンプレートは線形レイアウトで、水平方向と3つのTextViewコントロールが私たちの3つの財産価値を持っています.縮小、コピー
xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:id="@+id/rootLayout">
<TextViewandroid:id="@+id/nameLabel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10px"android:width="100px"android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextViewandroid:id="@+id/descriptionLabel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:width="150px"android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextViewandroid:id="@+id/priceLabel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:width="50px"android:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
このテンプレートは、リスト項目を膨張させるために使用されます.各アダプタはこのテンプレートを呼び出し、異なる方法で埋め込むことができますが、外観は同じで、インタフェースの一貫性を許可します.次に、各アダプタがbaseadapter>を拡張するリストアダプタを作成する必要があります.簡潔化のため、MetypeListAdapter、VegetableType-ListAdapter、FruitType-ListAdapterはType>強制タイプ変換を除きます.非表示、収縮、アダプタのスケルトンはGetViewメソッドです.指定した場所のアイテムを受け入れ、定義したタイプのビューを作成し、アイテムのプロパティ値を使用してコントロールを埋めます.このメソッドを作成して、計画で使用されるテンプレートとデータのアイテムタイプを正しく処理する必要があります.LinearLayoutタイプ私たちの例では、異なるルートレイアウトタイプ、さらには基本的な制御タイプを使用すると、コードはこのような変更を反映する必要があります!(LinearLayoutビュー)制御タイプにかかわらず、適切なビュータイプを提供します.条項で回収が許可されているコードが実行されている場合、LinearLayoutがすでに通過している場合、同じビュータイプGetViewが連続的に呼び出されている場合、インフレは次だけでなく、SectionedListAdapterを作成して複数のリストを処理する必要があります.リストビューコントロールに含める必要があります.このアダプタを作成する前に、個別のリストサブセクションを記述するためにListSectionクラスが必要です.のListSectionクラスは、セクションのテキストタイトル、セクションの列ラベル名、およびListAdapterを保存し、このセクションとともに使用します.非表示にします.同様に、セグメントアダプタを作成する前に、セグメントヘッダーまたはセパレータを記述するxmlテンプレートが必要です.これを実現するには、TextViewを簡単に使用できます.セパレータビュースタイルラベル「?android:attr/listSeparatorTextViewStyle」を設定すると、セパレータビューの下部枠線にセパレータ枠線が配置されます.この例では、区切り記号にも列ヘッダーが含まれているので、このテンプレートは簡単なTextViewよりも複雑になります.xmlは、縮小、コピーコードのように見えます.
xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/rootLayout"android:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="vertical">
<TextViewandroid:id="@+id/caption"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_marginTop="10px"android:textAppearance="?android:attr/textAppearanceSmall"/>
<LinearLayoutandroid:id="@+id/columnHeaderLayout"android:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="horizontal"style="?android:attr/listSeparatorTextViewStyle">
<TextViewandroid:id="@+id/columnHeader1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:marginLeft="10px"android:width="100px"android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextViewandroid:id="@+id/columnHeader2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:width="150px"android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextViewandroid:id="@+id/columnHeader3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:width="50px"android:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>
</LinearLayout>
内部LinearLayoutが設定されており、listSeperatorTextViewStyleとして列ヘッダーが保存されているため、LinearLayout全体が境界線を下に得ていることがわかります.セグメントアダプタ、SectionedListAdapterHide、縮小、codeのコピーを作成する準備ができています.
using Android.App;
using Android.Widget;
using Android.Views;
public class SectionedListAdapter
{
private const int TYPE_SECTION_HEADER = 0;
private Context _context;
private LayoutInflater _inflater;
private List _sections;
public SectionedListAdapter(Context context)
{
_context = context;
_inflater = LayoutInflater.From(_context);
_sections = new List();
}
public List Sections { get { return _sections; } set { _sections = value; } }
// Each section has x list items + 1 list item for the caption. This is the reason for the +1 in the tally
public override int Count
{
get
{
int count = 0;
foreach (ListSection s in _sections) count += s.Adapter.Count + 1;
return count;
}
}
// We know there will be at least 1 type, the seperator, plus each
// type for each section, that is why we start with 1
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]; } }
// Since we dont want the captions selectable or clickable returning a hard false here achieves this
public override bool AreAllItemsEnabled() { 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)
{
// postion == 0 means we have to inflate the section separator
if (position == 0)
{
if (view == null || !(view is LinearLayout))
{
view = _inflater.Inflate(Resource.Layout.ListSeparator, parent, false);
}
TextView caption = view.FindViewById(Resource.Id.caption);
caption.Text = s.Caption;
TextView columnHeader1 = view.FindViewById(Resource.Id.columnHeader1);
columnHeader1.Text = s.ColumnHeader1;
TextView columnHeader2 = view.FindViewById(Resource.Id.columnHeader2);
columnHeader2.Text = s.ColumnHeader2;
TextView columnHeader3 = view.FindViewById(Resource.Id.columnHeader3);
columnHeader3.Text = s.ColumnHeader3;
}
int size = s.Adapter.Count + 1;
// postion < size means we are at an item, so we just pass through its View from its adapter
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; // this is a separator item, dont want it instantiated
int size = s.Adapter.Count + 1;
if (position < size) return s.Adapter.GetItem(position);
position -= size;
}
return null;
}
}
ご覧のように、この例ではGetItemを上書きする必要があります.デフォルトではこの(int)が返されるため、通常はそうする必要はありません.しかし、我々のセグメントアダプタでは、この[int]はリストビューからリストアイテムを取得しようとすると機能しないListSectionオブジェクトを返します.上記のように書き換えると、この方法は適切なサブリストに深く入り込み、適切なオブジェクトを返すために必要なのは、これらの情報をすべて格納する必要があるListViewを埋め込むことである.次のコードクリップはApp.HideのOnCreateメソッドから来ています.コードのコピー
// Fist lets create and populate the List<> instances that hold our food items
List meats = new List();
meats.Add(new MeatType("Hamburger", "Ground chuck beef", 2.76));
meats.Add(new MeatType("Sirloin", "Sliced sirloin steaks", 4.56));
List veggies = new Listnew VegetableType("Brocolli", "Cut brocolli floretes", 1.76));
veggies.Add(new VegetableType("Carrots", "Cut peeled baby carrots", 2.18));
List fruits = new List();
fruits.Add(new FruitType("Apple", "Granny smith apples", 0.87));
fruits.Add(new FruitType("Peach", "South Carolina peaches", 1.12));
// Now we create our adapters for the item types
MeatTypeListAdapter madptr = new MeatTypeListAdapter(this, Resource.Layout.FoodTypeListItem, meats);
VegetableTypeListAdapter vadptr = new VegetableTypeListAdapter(this, Resource.Layout.FoodTypeListItem, veggies);
FruitTypeListAdapter fadptr = new FruitTypeListAdapter(this, Resource.Layout.FoodTypeListItem, fruits);
// Now we create our sectioned adapter and add its sections
SectionedListAdapter sadptr = new SectionedListAdapter(this);
sadptr.AddSection("Available Meats", "Name", "Description", "Price (lb.)", madptr);
sadptr.AddSection("Available Vegetables", "Name", "Description", "Price (lb.)", vadptr);
sadptr.AddSection("Available Fruits", "Name", "Description", "Price (lb.)", fadptr);
// Now fetch the ListView and set its adapter to the sectioned adapter
ListView foodList = FindViewById(Resource.Id.foodList);
foodList.SetAdapter(sadptr);
foodList.ItemClick += new EventHandler(foodList_ItemClick);
最後のステップに進み、ItemClickイベントを正しく処理します.ItemClickイベントがトリガーされたときに正しいサブリストがクエリされることを確認する必要があります.この例では、クリックしたエントリの詳細を含む新しいレイアウトを表示します.次はItemClickイベントハンドラのコアです.これを実現するためにもっと良い方法があるかもしれませんが、Java.Langを強制的に変換する明確な方法を見ていません.オブジェクトをに変換します.Netオブジェクト.このため,2つのToString法の出力を比較した.データ・オブジェクトにたまたまカスタムのToStringメソッドがある場合は、少し調整する必要があります.非表示、コードのコピー
private void foodList_ItemClick(object sender, AdapterView.ListItemClickEventArgs e)
{
SectionedListAdapter adptr = (sender as ListView).Adapter as SectionedListAdapter;
if (adptr.GetItem(e.Position != null)
{
if (adptr.GetItem(e.Position).ToString() == typeof(MeatType).ToString())
{
// Handle your code however you like here for when a meat is clicked
}
else if (adptr.GetItem(e.Position).ToString() == typeof(VegetableType).ToString())
{
// Handle your code however you like here for when a vegetable is clicked
}
else if (adptr.GetItem(e.Position).ToString() == typeof(FruitType).ToString())
{
// Handler your code however you like here for when a fruit is clicked
}
}
}
Update--クリックを汎用的に反応させたい場合にのみ、この方法が本当に役に立つことに気づきました.クリックされたオブジェクトを処理する必要がある場合は、少し厄介になりますが、悪くはありません.このためには、Java.Langのチャネルを処理するためにパッケージクラスを作成する必要があります.オブジェクトインスタンスは、アダプタのGetItem(int)メソッドを上書きするときに渡されます.まず、パッケージングクラスJavaObjectHandlerHideレプリケーションコード
using Java.Lang;
using System;
public class JavaObjectHandler : Java.Lang.Object
{
private System.Object _instance;
public JavaObjectHandler(System.Object instance) { _instance = instance; }
public System.Object Instance { get { return _instance; } }
}
Nowでは、アダプタ内のGetItem(int)メソッドを上書きすると、デフォルトのJava.LangではなくJavaObjectHandlerインスタンスが渡されます.オブジェクトの例です.JavaObjectHandlerには、アダプタに隠されているデータオブジェクトが含まれます.baseadapter>クラスに戻り、次の方法を追加してcodeをコピーします.
public override Java.Lang.Object GetItem(int position)
{
if (position < _items.Count)
{
return new JavaObjectHandler(_items[position]);
}
return null;
}
これは、SectionedListAdapterで上書きされているため、セパレータをクリックするとnullまたはデータ・オブジェクトを含む最下位のJavaObjectHandlerが渡されます.ItemClickイベントを変更して、hideレプリケーションcodeなどの新しいJavaObjectHandlerインスタンスを使用できます.
private void foodList_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
SectionedListAdapter adapter = (sender as ListView).Adapter as SectionedListAdapter;
JavaObjectHandler item = adapter.GetItem(e.Position);
if (item != null)
{
if (item.Instance is MeatType)
{
Toast.MakeText(this, "You clicked a meat: " +
(item.Instance as MeatType).Name, ToastLength.Short).Show();
}
else if (item.Instance is VegetableType)
{
Toast.MakeText(this, "You clicked a vegetable: " +
(item.Instance as VegetableType).Name, ToastLength.Short).Show();
}
else if (item.Instance is FruitType)
{
Toast.MakeText(this, "You clicked a fruit: " +
(item.Instance as FruitType).Name, ToastLenght.Short).Show();
}
}
}
このセグメントアダプタは、ListViewまたはListActivityと同様に使用することができる.History 1.0-2012年12月7日-一般的な操作が発表されました.ソースコードとサンプルアイテムについては後述します.1.1-12/17/2012-更新は、より高度なJavaObjectHandlerのItemClickイベント処理の使用を反映します.サンプルアイテムを記事に追加します.SampleはAndroid 4.0.3のために構築されています.転載先:http://www.diyabc.com/frontweb/news30838.html