Заполнение Xamarin.Android ListView данными

Чтобы добавить строки в ListView макет, необходимо добавить его в макет и реализовать IListAdapter методы, которые ListView вызываются для заполнения. Android включает встроенные ListActivity и ArrayAdapter классы, которые можно использовать без определения xml-кода пользовательского макета или кода. Класс ListActivity автоматически создает ListView и предоставляет ListAdapter свойство для предоставления представлений строк для отображения через адаптер.

Встроенные адаптеры принимают идентификатор ресурса представления в качестве параметра, который используется для каждой строки. Вы можете использовать встроенные ресурсы, такие как те, в Android.Resource.Layout которых нет необходимости писать собственные ресурсы.

Использование строки ListActivity и ArrayAdapter<>

В примере BasicTable/HomeScreen.cs показано, как использовать эти классы для отображения ListView только в нескольких строках кода:

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

Обработка щелчков строк

ListView Обычно пользователь также позволяет пользователю касаться строки для выполнения некоторых действий (таких как воспроизведение песни, вызов контакта или отображение другого экрана). Чтобы реагировать на касания пользователей, необходимо еще один метод, реализованный в ListActivityOnListItemClick следующем:

Screenshot of a 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();
}

Теперь пользователь может коснуться строки и появится оповещение Toast :

Screenshot of Toast that appears when a row is touched

Реализация ListAdapter

ArrayAdapter<string> отлично из-за его простоты, но это чрезвычайно ограничено. Однако часто у вас есть коллекция бизнес-сущностей, а не только строк, которые требуется привязать. Например, если данные состоят из коллекции классов Employee, может потребоваться, чтобы список отображал только имена каждого сотрудника. Чтобы настроить поведение ListView элемента управления отображаемыми данными, необходимо реализовать подкласс BaseAdapter переопределения следующих четырех элементов:

  • Количество . Чтобы сообщить элементу управления, сколько строк находятся в данных.

  • GetView — чтобы вернуть представление для каждой строки, заполненной данными. Этот метод имеет параметр для ListView передачи существующей неиспользуемой строки для повторного использования.

  • GetItemId — возвращает идентификатор строки (обычно номер строки, хотя это может быть любое длинное значение, которое вы хотите).

  • индексатор [int] — чтобы вернуть данные, связанные с определенным номером строки.

Пример кода в BasicTableAdapter/HomeScreenAdapter.cs демонстрирует, как подкласс 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;
   }
}

Использование пользовательского адаптера

Использование пользовательского адаптера аналогично встроенному ArrayAdapter, передавая значения context и string[] отображаемые значения:

ListAdapter = new HomeScreenAdapter(this, items);

Так как в этом примере используется тот же макет строки (SimpleListItem1) результирующее приложение будет выглядеть идентично предыдущему примеру.

Повторное использование представления строк

В этом примере есть только шесть элементов. Так как экран может соответствовать восьми, не требуется повторного использования строк. При отображении сотен или тысяч строк, однако, это была бы трата памяти, чтобы создать сотни или тысячи View объектов, когда только восемь помещаются на экране одновременно. Чтобы избежать этой ситуации, когда строка исчезает с экрана, его представление помещается в очередь для повторного использования. По мере прокрутки пользователя вызовы GetView для запроса новых представлений для отображения — если он доступен, ListView он передает неиспользуемое представление в параметреconvertView. Если это значение равно NULL, код должен создать новый экземпляр представления, в противном случае можно повторно задать свойства этого объекта и повторно использовать его.

Метод GetView должен соответствовать этому шаблону для повторного использования представлений строк:

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

Реализации пользовательских адаптеров всегда следует повторно использовать convertView объект перед созданием новых представлений, чтобы убедиться, что при отображении длинных списков не хватает памяти.

Некоторые реализации адаптера (напримерCursorAdapter, метод) не имеют GetView метода, а требуют двух разных методов и BindView принудительного повторного использования строк путем разделения обязанностей GetView двух методовNewView. Далее в документе есть CursorAdapter пример.

Включение быстрой прокрутки

Быстрая прокрутка помогает пользователю прокручивать длинные списки, предоставляя дополнительный "дескриптор", который выступает в качестве полосы прокрутки для прямого доступа к части списка. На снимком снимка экрана показана быстрая обработка прокрутки:

Screenshot of fast-scrolling with a scroll handle

Чтобы быстро прокручивающийся дескриптор отображалось так же просто, как задать FastScrollEnabled для свойства trueзначение :

ListView.FastScrollEnabled = true;

Добавление индекса раздела

Индекс раздела предоставляет дополнительные отзывы для пользователей при быстрой прокрутке по длинному списку— он показывает, на какой раздел они прокрутили. Чтобы индекс раздела отображался подкласс Адаптера, необходимо реализовать ISectionIndexer интерфейс для предоставления текста индекса в зависимости от отображаемых строк:

Screenshot of H appearing above section that starts with H

Чтобы реализовать ISectionIndexer , необходимо добавить три метода в адаптер:

  • GetSections — предоставляет полный список заголовков индексов разделов, которые можно отобразить. Для этого метода требуется массив объектов Java, поэтому коду необходимо создать из Java.Lang.Object[] коллекции .NET. В нашем примере возвращается список начальных символов в списке как Java.Lang.String .

  • GetPositionForSection — возвращает первую позицию строки для заданного индекса раздела.

  • GetSectionForPosition — возвращает индекс раздела, отображаемый для заданной строки.

Пример SectionIndex/HomeScreenAdapter.cs файла реализует эти методы и дополнительный код в конструкторе. Конструктор создает индекс раздела путем цикла по каждой строке и извлечения первого символа заголовка (элементы уже должны быть отсортированы для работы).

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

При создании ISectionIndexer структур данных методы очень просты:

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

Названия индексов разделов не должны сопоставляться с фактическими разделами. Именно поэтому GetPositionForSection метод существует. GetPositionForSection позволяет сопоставить все индексы в списке индексов с любыми разделами в представлении списка. Например, у вас может быть "z" в индексе, но у вас может не быть раздела таблицы для каждой буквы, поэтому вместо "z" сопоставление с 26 может быть сопоставлено с 25 или 24, или любой индекс раздела "z" должен сопоставляться с.