Popolamento di un controllo ListView Xamarin.Android con dati
Per aggiungere righe a un ListView
oggetto è necessario aggiungerlo al layout e implementare un oggetto IListAdapter
con metodi che le ListView
chiamate popolano autonomamente. Android include classi e ArrayAdapter
predefinite ListActivity
che è possibile usare senza definire codice o XML di layout personalizzato. La ListActivity
classe crea automaticamente un oggetto ListView
ed espone una ListAdapter
proprietà per fornire le visualizzazioni di riga da visualizzare tramite un adattatore.
Gli adattatori predefiniti accettano un ID risorsa di visualizzazione come parametro che viene usato per ogni riga. È possibile usare risorse predefinite, ad esempio quelle in Android.Resource.Layout
, in modo da non dover scrivere le proprie.
Uso di ListActivity e della stringa ArrayAdapter<>
Nell'esempio BasicTable/HomeScreen.cs viene illustrato come usare queste classi per visualizzare un oggetto ListView
in poche righe di codice:
[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);
}
}
Gestione dei clic delle righe
In genere un ListView
consentirà anche all'utente di toccare una riga per eseguire un'azione (ad esempio la riproduzione di una canzone, o la chiamata a un contatto o la visualizzazione di un'altra schermata). Per rispondere ai tocco dell'utente, è necessario implementare un altro metodo in ListActivity
, OnListItemClick
come illustrato di seguito:
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();
}
Ora l'utente può toccare una riga e verrà visualizzato un Toast
avviso:
Implementazione di un listAdapter
ArrayAdapter<string>
è grande a causa della sua semplicità, ma è estremamente limitato. Tuttavia, spesso si dispone di una raccolta di entità aziendali, anziché solo di stringhe da associare.
Ad esempio, se i dati sono costituiti da una raccolta di classi Employee, è possibile che l'elenco visualizzi solo i nomi di ogni dipendente. Per personalizzare il comportamento di un ListView
oggetto per controllare i dati visualizzati, è necessario implementare una sottoclasse dell'override dei BaseAdapter
quattro elementi seguenti:
Count : per indicare al controllo il numero di righe presenti nei dati.
GetView : per restituire una visualizzazione per ogni riga, popolata con i dati. Questo metodo ha un parametro per il
ListView
passaggio di una riga esistente inutilizzata per il riutilizzo.GetItemId : restituisce un identificatore di riga (in genere il numero di riga, anche se può essere qualsiasi valore lungo desiderato).
indicizzatore this[int] : per restituire i dati associati a un determinato numero di riga.
Il codice di esempio in BasicTableAdapter/HomeScreenAdapter.cs illustra come sottoclasse 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;
}
}
Uso di un adattatore personalizzato
L'uso dell'adattatore personalizzato è simile al valore predefinito ArrayAdapter
, passando un context
e i string[]
valori da visualizzare:
ListAdapter = new HomeScreenAdapter(this, items);
Poiché in questo esempio viene usato lo stesso layout di riga (SimpleListItem1
) l'applicazione risultante sarà identica all'esempio precedente.
Riesezione della visualizzazione riga
In questo esempio sono presenti solo sei elementi. Poiché lo schermo può adattarsi a otto, non è necessario riutilizzare la riga. Quando si visualizzano centinaia o migliaia di righe, tuttavia, si tratta di uno spreco di memoria per creare centinaia o migliaia di View
oggetti quando solo otto si adattano allo schermo alla volta. Per evitare questa situazione, quando una riga scompare dallo schermo, la visualizzazione viene inserita in una coda per il riutilizzo. Quando l'utente scorre, le ListView
chiamate GetView
per richiedere la visualizzazione di nuove visualizzazioni, se disponibili passano una visualizzazione inutilizzata nel convertView
parametro . Se questo valore è Null, il codice deve creare una nuova istanza di visualizzazione. In caso contrario, è possibile riimpostati le proprietà dell'oggetto e usarlo di nuovo.
Il GetView
metodo deve seguire questo modello per riutilizzare le visualizzazioni di riga:
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;
}
Le implementazioni dell'adattatore personalizzato devono sempre riutilizzare l'oggetto convertView
prima di creare nuove visualizzazioni per assicurarsi che non esauriscano la memoria durante la visualizzazione di elenchi lunghi.
Alcune implementazioni dell'adapter CursorAdapter
(ad esempio ) non hanno un GetView
metodo, ma richiedono due metodi NewView
diversi e BindView
che impongono il riutilizzo delle righe separando le responsabilità di GetView
in due metodi. Più avanti nel documento è disponibile un CursorAdapter
esempio.
Abilitazione dello scorrimento rapido
Lo scorrimento rapido consente all'utente di scorrere gli elenchi lunghi fornendo un 'handle' aggiuntivo che funge da barra di scorrimento per accedere direttamente a una parte dell'elenco. Questo screenshot mostra l'handle di scorrimento rapido:
La visualizzazione dell'handle di scorrimento rapido è semplice come l'impostazione della FastScrollEnabled
proprietà su true
:
ListView.FastScrollEnabled = true;
Aggiunta di un indice di sezione
Un indice di sezione fornisce un feedback aggiuntivo per gli utenti quando scorre rapidamente un lungo elenco, che mostra la "sezione" a cui è stato eseguito lo scorrimento. Per fare in modo che l'indice di sezione venga visualizzato nella sottoclasse Adapter deve implementare l'interfaccia ISectionIndexer
per fornire il testo dell'indice a seconda delle righe visualizzate:
Per implementare ISectionIndexer
è necessario aggiungere tre metodi a un adattatore:
GetSections : fornisce l'elenco completo dei titoli degli indici di sezione che possono essere visualizzati. Questo metodo richiede una matrice di oggetti Java in modo che il codice debba creare un oggetto
Java.Lang.Object[]
da una raccolta .NET. Nell'esempio viene restituito un elenco dei caratteri iniziali nell'elenco comeJava.Lang.String
.GetPositionForSection : restituisce la prima posizione della riga per un indice di sezione specificato.
GetSectionForPosition : restituisce l'indice di sezione da visualizzare per una determinata riga.
Il file di esempio SectionIndex/HomeScreenAdapter.cs
implementa tali metodi e un codice aggiuntivo nel costruttore. Il costruttore compila l'indice di sezione eseguendo un ciclo in ogni riga ed estraendo il primo carattere del titolo (gli elementi devono essere già ordinati per il funzionamento di questa operazione).
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]);
}
Con le strutture di dati create, i ISectionIndexer
metodi sono molto semplici:
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;
}
I titoli degli indici di sezione non devono eseguire il mapping di 1:1 alle sezioni effettive. Ecco perché il GetPositionForSection
metodo esiste.
GetPositionForSection
offre l'opportunità di eseguire il mapping di qualsiasi indice nell'elenco di indici a qualsiasi sezione della visualizzazione elenco. Ad esempio, è possibile che nell'indice sia presente una sezione "z", ma potrebbe non essere presente una sezione della tabella per ogni lettera, quindi anziché il mapping "z" a 26, potrebbe essere mappato a 25 o 24 o qualsiasi indice di sezione "z" a cui eseguire il mapping.