Naplnění ovládacího prvku Xamarin. Android s daty

Chcete-li přidat řádky do objektu, ListView je nutné jej přidat do rozložení a implementovat IListAdapter metody, které ListView volání naplní. Android obsahuje integrované ListActivity a ArrayAdapter třídy, které můžete použít bez definování vlastního rozložení XML nebo kódu. ListActivityTřída automaticky vytvoří ListView a zpřístupní ListAdapter vlastnost pro poskytnutí zobrazení řádků pro zobrazení prostřednictvím adaptéru.

Vestavěné adaptéry přebírají ID prostředku zobrazení jako parametr, který se používá pro každý řádek. Můžete použít předdefinované prostředky, jako jsou ty, Android.Resource.Layout které nemusíte psát sami.

Použití ListActivity a ArrayAdapter < String>

Příklad základní/HomeScreen. cs ukazuje, jak použít tyto třídy k zobrazení v pouze pár řádků 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

Obvykle ListView by uživatel mohl také uživateli podotknout řádek, aby provedl určitou akci (například přehrání skladby nebo volání kontaktu nebo zobrazení jiné obrazovky). Chcete-li reagovat na dotyky uživatele, je nutné, aby byla v následujícím rozhraní implementována jedna další metoda 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 výstraha:

Snímek obrazovky s informačními zprávami, které se zobrazí, když se řádek dotkne

Implementace ListAdapter

ArrayAdapter<string> je skvělé z důvodu jeho jednoduchosti, ale je to extrémně omezené. Nicméně často máte kolekci obchodních entit, nikoli jenom řetězce, které chcete vytvořit. Například pokud se data skládají z kolekce tříd zaměstnanců, můžete chtít, aby se v seznamu zobrazila pouze jména jednotlivých zaměstnanců. Chcete-li přizpůsobit chování ovládacího ListView prvku pro řízení zobrazených dat, je nutné implementovat podtřídu pro BaseAdapter přepsání následujících čtyř položek:

  • Count – pro sdělení ovládacího prvku, kolik řádků je v datech.

  • GetView – pro vrácení zobrazení pro každý řádek vyplněný daty. Tato metoda má parametr pro ListView předání v existujícím nepoužitém řádku pro opakované 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 – vrátí data přidružená k určitému číslu řádku.

Vzorový kód v BasicTableAdapter/HomeScreenAdapter. cs ukazuje, jak podtřídit :

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 je podobné vestavěnému, předává se ArrayAdaptercontext a string[] hodnotám, které se mají zobrazit:

ListAdapter = new HomeScreenAdapter(this, items);

Vzhledem k tomu, že v tomto příkladu se 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 tu jenom šest položek. Vzhledem k tomu, že se obrazovka vejde na osm, nevyžaduje opakované použití řádků. Při zobrazování stovek nebo tisíců řádků by se ale mohlo jednat o odpad paměti, aby se vytvořily stovky nebo tisíce View objektů, když se na obrazovku vejde jenom osm. Chcete-li se této situaci vyhnout, v případě, že řádek zmizí z obrazovky, je jeho zobrazení umístěno ve frontě pro opakované použití. Při posunu uživatele se ListView zobrazí volání, která GetView vyžádají nové zobrazení k zobrazení – Pokud je k dispozici, předává nepoužívané zobrazení v convertView parametru. Pokud je tato hodnota null, váš kód by měl vytvořit novou instanci zobrazení, jinak můžete znovu nastavit vlastnosti daného objektu a znovu jej použít.

GetViewMetoda by měla postupovat podle tohoto vzoru pro opětovné 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 vždy znovu použít objekt před vytvořením nových zobrazení a zajistit, aby při zobrazování dlouhých seznamů nedošly paměti.

Některé implementace adaptérů (například) nemají CursorAdapterGetView metodu, ale vyžadují dvě různé metody NewView a BindView Vynutit opakované použití řádku oddělením odpovědností GetView do dvou metod. CursorAdapterV dokumentu je příklad dále.

Povolení rychlého posouvání

Rychlé posouvání pomáhá uživateli procházet dlouhé seznamy tím, že poskytuje další "popisovač", který funguje jako posuvník pro přímý přístup k části seznamu. Na tomto snímku obrazovky se zobrazuje úchyt pro rychlé posouvání:

Snímek obrazovky s rychlým posouváním pomocí rolovacího úchytu

Způsob, jakým se má zobrazit úchyt pro rychlé posouvání, je 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 při rychlém procházení dlouhým seznamem – zobrazuje, který oddíl se přesunul na. Aby se zobrazil index oddílu, musí podtřída adaptéru implementovat ISectionIndexer rozhraní a zadat indexový text v závislosti na zobrazených řádcích:

Snímek obrazovky H se zobrazeným oddílem h, který začíná na H

K implementaci ISectionIndexer musíte do adaptéru přidat tři metody:

  • GetSections – poskytuje úplný seznam názvů oddílů indexu, které se dají zobrazit. Tato metoda vyžaduje pole objektů Java, aby kód mohl 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í rejstřík 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 vytvoří index oddílu cyklickým cyklem prostřednictvím každého řádku a extrahováním prvního znaku nadpisu (položky musí být již řazeny, aby fungovaly).

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]);
}

V případě vytvořených datových struktur jsou tyto ISectionIndexer 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 oddílů indexu nemusejí namapovat 1:1 na vaše skutečné oddíly. To je důvod, proč GetPositionForSection existuje metoda. GetPositionForSection poskytuje příležitost k mapování všech indexů, které jsou v seznamu indexů, na jakékoli oddíly v zobrazení seznamu. Například můžete mít v indexu "z", ale nesmíte mít oddíl tabulky pro každé písmeno, takže místo "z" mapování na 26, může být mapováno na 25 nebo 24 nebo jakýkoli index oddílu "z" by měl být namapován na.