Naplnění objektu ListView Xamarin.Android daty
Pokud chcete přidat řádky do ListView
svého rozložení, musíte ho přidat do svého rozložení a implementovat IListAdapter
s metodami, které ListView
se budou naplnit voláním samotného. Android obsahuje integrované ListActivity
třídy a ArrayAdapter
třídy, které můžete použít bez definování jakéhokoli vlastního rozložení XML nebo kódu. Třída ListActivity
automaticky vytvoří ListView
a zpřístupní ListAdapter
vlastnost pro zadání zobrazení řádků, která se mají zobrazit přes adaptér.
Předdefinované adaptéry přebírají ID prostředku zobrazení jako parametr, který se použije pro každý řádek. Můžete použít předdefinované prostředky, jako jsou ty, Android.Resource.Layout
abyste nemuseli psát vlastní.
Použití řetězce ListActivity a ArrayAdapter<>
Příklad BasicTable/HomeScreen.cs ukazuje, jak tyto třídy použít k zobrazení ListView
pouze v několika řádcích kódu:
[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);
}
}
Zpracování kliknutí na řádek
ListView
Obvykle také umožní uživateli, aby se dotknul řádku, aby provedl určitou akci (například přehrávání skladby nebo volání kontaktu nebo zobrazení jiné obrazovky). Aby uživatel reagoval na dotyky, musí existovat jedna další metoda implementovaná v této 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();
}
Teď se uživatel může dotýkat řádku a Toast
zobrazí se upozornění:
Implementace prvku ListAdapter
ArrayAdapter<string>
je skvělá kvůli své jednoduchosti, ale je velmi omezená. Často však máte kolekci obchodních entit, nikoli jenom řetězce, které chcete svázat.
Pokud se například vaše data skládají z kolekce tříd zaměstnanců, můžete chtít, aby seznam zobrazoval jenom jména jednotlivých zaměstnanců. Chcete-li přizpůsobit chování ListView
pro řízení zobrazených dat, musíte implementovat podtřídu BaseAdapter
přepsání následujících čtyř položek:
Count – To tell the control how many rows are in the data.
GetView – Pokud chcete vrátit zobrazení pro každý řádek, naplněné daty. Tato metoda má parametr pro
ListView
předání existujícího nepoužívaného řádku pro opětovné použití.GetItemId – Vrátí identifikátor řádku (obvykle číslo řádku, i když může být libovolná dlouhá hodnota, kterou chcete).
this[int] indexer – chcete-li vrátit data přidružená k určitému číslu řádku.
Příklad kódu v BasicTableAdapter/HomeScreenAdapter.cs ukazuje, jak podtřídu 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;
}
}
Použití vlastního adaptéru
Použití vlastního adaptéru se podobá integrovanému ArrayAdapter
předávání context
hodnot a string[]
hodnotám, které se mají zobrazit:
ListAdapter = new HomeScreenAdapter(this, items);
Vzhledem k tomu, že tento příklad používá stejné rozložení řádků (SimpleListItem1
), bude výsledná aplikace vypadat stejně jako v předchozím příkladu.
Opětovné použití zobrazení řádků
V tomto příkladu je pouze šest položek. Vzhledem k tomu, že obrazovka se může vejít do osmi, není nutné znovu používat žádné řádky. Při zobrazení stovek nebo tisíců řádků by však bylo plýtvání pamětí vytvořit stovky nebo tisíce View
objektů, když se na obrazovku najednou vejde jen osm. Pokud se chcete této situaci vyhnout, když řádek zmizí z obrazovky, jeho zobrazení se umístí do fronty pro opětovné použití. Když se uživatel posune, ListView
volání GetView
k vyžádání nových zobrazení k zobrazení – pokud je k dispozici, předá nepoužité zobrazení v parametru convertView
. Pokud má tato hodnota hodnotu null, měl by váš kód vytvořit novou instanci zobrazení, jinak můžete znovu nastavit vlastnosti tohoto objektu a znovu ho použít.
Metoda GetView
by měla postupovat podle tohoto vzoru pro opakované použití zobrazení řádků:
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;
}
Implementace vlastních adaptérů by měly před vytvořením nových zobrazení vždy znovu použít convertView
objekt, aby se zajistilo, že při zobrazování dlouhých seznamů nevyčerpaly paměť.
Některé implementace adaptérů (například CursorAdapter
) nemají metodu GetView
, místo toho vyžadují dvě různé metody NewView
a BindView
které vynucují opětovné použití řádků oddělením zodpovědností GetView
do dvou metod. Příklad najdete CursorAdapter
později v dokumentu.
Povolení rychlého posouvání
Rychlé posouvání pomáhá uživateli procházet dlouhé seznamy tím, že poskytuje další popisovač, který slouží jako posuvník pro přímý přístup k části seznamu. Tento snímek obrazovky ukazuje rychlý úchyt pro posouvání:
Způsob zobrazení úchytu pro rychlé posouvání je stejně jednoduchý jako nastavení FastScrollEnabled
vlastnosti na true
:
ListView.FastScrollEnabled = true;
Přidání indexu oddílu
Index oddílu poskytuje uživatelům další zpětnou vazbu, když rychle prochází dlouhým seznamem – ukazuje, na který oddíl se posouvali. Aby se index oddílu zobrazil, musí podtřída Adaptér implementovat ISectionIndexer
rozhraní pro zadání textu indexu v závislosti na zobrazených řádcích:
Pokud chcete implementovat ISectionIndexer
, musíte do adaptéru přidat tři metody:
GetSections – Poskytuje úplný seznam názvů indexů oddílů, které lze zobrazit. Tato metoda vyžaduje pole objektů Java, aby kód potřeboval vytvořit
Java.Lang.Object[]
z kolekce .NET. V našem příkladu vrátí seznam počátečních znaků v seznamu jakoJava.Lang.String
.GetPositionForSection – vrátí první pozici řádku pro daný index oddílu.
GetSectionForPosition – vrátí index oddílu, který se má zobrazit pro daný řádek.
Ukázkový SectionIndex/HomeScreenAdapter.cs
soubor implementuje tyto metody a další kód v konstruktoru. Konstruktor sestaví index oddílu tak, že prochází každý řádek a extrahuje první znak názvu (položky už musí být seřazené, aby to fungovalo).
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]);
}
S vytvořenými ISectionIndexer
datovými strukturami jsou metody velmi jednoduché:
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;
}
Názvy indexů oddílů nemusí mapovat 1:1 na skutečné oddíly. GetPositionForSection
Proto tato metoda existuje.
GetPositionForSection
umožňuje mapovat jakékoli indexy, které jsou v seznamu indexů, na libovolné oddíly v zobrazení seznamu. V indexu můžete mít například "z", ale možná nemáte oddíl tabulky pro každé písmeno, takže místo "z" mapování na hodnotu 26 se může namapovat na 25 nebo 24 nebo jakýkoli index oddílu "z".