Udostępnij za pośrednictwem


Wypełnianie obiektu ListView platformy Xamarin.Android przy użyciu danych

Aby dodać wiersze do elementu ListView , należy dodać go do układu i zaimplementować za IListAdapter pomocą metod, które ListView wywołania wypełniają się. System Android zawiera wbudowane ListActivity i ArrayAdapter klasy, których można używać bez definiowania niestandardowego kodu XML lub kodu układu niestandardowego. Klasa ListActivity automatycznie tworzy ListView właściwość i uwidacznia ListAdapter właściwość w celu dostarczenia widoków wierszy do wyświetlenia za pośrednictwem karty.

Wbudowane karty przyjmują identyfikator zasobu widoku jako parametr, który jest używany dla każdego wiersza. Możesz użyć wbudowanych zasobów, takich jak te, Android.Resource.Layout aby nie trzeba było pisać własnych.

Używanie ciągu ListActivity i ArrayAdapter<>

W przykładzie BasicTable/HomeScreen.cs pokazano , jak używać tych klas do wyświetlania ListView obiektu w zaledwie kilku wierszach kodu:

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

Obsługa kliknięć wierszy

Zazwyczaj użytkownik ListView może również dotknąć wiersza, aby wykonać jakąś akcję (np. odtwarzanie utworu lub wywoływanie kontaktu lub wyświetlanie innego ekranu). Aby reagować na dotykanie użytkownika, musi istnieć jeszcze jedna metoda zaimplementowana w ListActivity obiekcie — OnListItemClick w następujący sposób:

Zrzut ekranu przedstawiający element 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();
}

Teraz użytkownik może dotknąć wiersza i Toast pojawi się alert:

Zrzut ekranu wyskakujący wyświetlany po dotknięciu wiersza

Implementowanie elementu ListAdapter

ArrayAdapter<string> jest wielki ze względu na jego prostotę, ale jest bardzo ograniczona. Jednak często masz kolekcję jednostek biznesowych, a nie tylko ciągi, które chcesz powiązać. Jeśli na przykład dane składają się z kolekcji klas Employee, możesz chcieć wyświetlić tylko nazwy każdego pracownika. Aby dostosować zachowanie obiektu , ListView aby kontrolować, jakie dane są wyświetlane, należy zaimplementować podklasę BaseAdapter zastępowania następujących czterech elementów:

  • Count — określa, ile wierszy znajdują się w danych.

  • GetView — aby zwrócić widok dla każdego wiersza, wypełniony danymi. Ta metoda ma parametr umożliwiający ListView przekazanie istniejącego, nieużywanego wiersza do ponownego użycia.

  • GetItemId — zwraca identyfikator wiersza (zazwyczaj numer wiersza, chociaż może to być dowolna długa wartość, którą chcesz).

  • this[int] indexer — aby zwrócić dane skojarzone z określoną liczbą wierszy.

Przykładowy kod w klasie BasicTableAdapter/HomeScreenAdapter.cs pokazuje, jak podklasy 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;
   }
}

Używanie karty niestandardowej

Użycie karty niestandardowej jest podobne do wbudowanej ArrayAdapter, przekazując context wartości i string[] do wyświetlenia:

ListAdapter = new HomeScreenAdapter(this, items);

Ponieważ w tym przykładzie użyto tego samego układu wiersza (SimpleListItem1), wynikowa aplikacja będzie wyglądać identycznie jak w poprzednim przykładzie.

Ponowne użycie widoku wiersza

W tym przykładzie istnieje tylko sześć elementów. Ponieważ ekran może mieścić się w ośmiu, nie jest wymagane ponowne użycie wiersza. Podczas wyświetlania setek lub tysięcy wierszy byłoby to jednak strata pamięci do utworzenia setek lub tysięcy View obiektów, gdy na ekranie mieści się tylko osiem. Aby uniknąć tej sytuacji, gdy wiersz zniknie z ekranu, jego widok zostanie umieszczony w kolejce do ponownego użycia. Gdy użytkownik przewija, ListView wywołania GetView żądania wyświetlenia nowych widoków — jeśli są dostępne, przekazuje nieużywany widok w parametrze convertView . Jeśli ta wartość ma wartość null, kod powinien utworzyć nowe wystąpienie widoku, w przeciwnym razie można ponownie ustawić właściwości tego obiektu i ponownie go użyć.

Ta metoda powinna być zgodna GetView z tym wzorcem, aby ponownie używać widoków wierszy:

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

Implementacje kart niestandardowych powinny zawsze ponownie używać convertViewobiektu przed utworzeniem nowych widoków, aby upewnić się, że nie zabraknie pamięci podczas wyświetlania długich list.

Niektóre implementacje adaptera (takie jak CursorAdapter) nie mają GetView metody, a nie wymagają dwóch różnych metod NewView i BindView które wymuszają ponowne użycie wierszy, oddzielając obowiązki GetView dwóch metod. W dalszej części dokumentu znajduje CursorAdapter się przykład.

Włączanie szybkiego przewijania

Szybkie przewijanie pomaga użytkownikowi przewijać długie listy, zapewniając dodatkowy "uchwyt", który działa jako pasek przewijania, aby bezpośrednio uzyskać dostęp do części listy. Ten zrzut ekranu przedstawia szybki uchwyt przewijania:

Zrzut ekranu przedstawiający szybkie przewijanie z uchwytem przewijania

Powoduje, że szybkie przewijanie uchwytu jest tak proste, jak ustawienie FastScrollEnabled właściwości na true:

ListView.FastScrollEnabled = true;

Dodawanie indeksu sekcji

Indeks sekcji zawiera dodatkową opinię dla użytkowników, gdy szybko przewijają długą listę — pokazuje, do której sekcji się przewinęli. Aby spowodować wyświetlenie indeksu sekcji, podklasa Adapter musi zaimplementować ISectionIndexer interfejs, aby podać tekst indeksu w zależności od wyświetlanych wierszy:

Zrzut ekranu przedstawiający sekcję H, która zaczyna się od H

Aby zaimplementować ISectionIndexer , należy dodać trzy metody do karty:

  • GetSections — zawiera pełną listę tytułów indeksu sekcji, które mogą być wyświetlane. Ta metoda wymaga tablicy obiektów Java, więc kod musi utworzyć element Java.Lang.Object[] na podstawie kolekcji platformy .NET. W naszym przykładzie zwraca listę początkowych znaków na liście jako Java.Lang.String .

  • GetPositionForSection — zwraca pozycję pierwszego wiersza dla danego indeksu sekcji.

  • GetSectionForPosition — zwraca indeks sekcji, który ma być wyświetlany dla danego wiersza.

Przykładowy SectionIndex/HomeScreenAdapter.cs plik implementuje te metody i dodatkowy kod w konstruktorze. Konstruktor tworzy indeks sekcji, tworząc pętlę w każdym wierszu i wyodrębniając pierwszy znak tytułu (elementy muszą być już posortowane, aby to działało).

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

Po utworzeniu ISectionIndexer struktur danych metody są bardzo proste:

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

Tytuły indeksu sekcji nie muszą być mapowane na rzeczywiste sekcje 1:1. GetPositionForSection Dlatego metoda istnieje. GetPositionForSection Daje możliwość mapowania indeksów na listę indeksów do dowolnych sekcji w widoku listy. Na przykład w indeksie może istnieć "z", ale może nie mieć sekcji tabeli dla każdej litery, więc zamiast "z" mapowania na 26, może być mapowanie na 25 lub 24, lub niezależnie od indeksu sekcji "z" powinno być mapowanie na.