Sdílet prostřednictvím


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 ListActivityOnListItemClick :

Snímek obrazovky s 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();
}

Teď se uživatel může dotýkat řádku a Toast zobrazí se upozornění:

Snímek obrazovky s informační zprávou, která se zobrazí při dotyku řádku

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 ArrayAdapterpř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í:

Snímek obrazovky s rychlým posouváním pomocí úchytu 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:

Snímek obrazovky S obrázkem H, který se zobrazuje nad oddílem, který začíná na H

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 jako Java.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".