Preenchendo um ListView Xamarin.Android com dados
Para adicionar linhas a um ListView
você precisa adicioná-lo ao seu layout e implementar um IListAdapter
com métodos que as chamadas para preencher a ListView
si mesmo. O Android inclui classes internas ListActivity
que ArrayAdapter
você pode usar sem definir nenhum layout, XML ou código personalizado. A ListActivity
classe cria automaticamente uma ListView
e expõe uma ListAdapter
propriedade para fornecer as exibições de linha a serem exibidas por meio de um adaptador.
Os adaptadores internos usam uma ID de recurso de exibição como um parâmetro que é usado para cada linha. Você pode usar recursos internos, como esses Android.Resource.Layout
, para não precisar escrever seus próprios.
Usando ListActivity e ArrayAdapter<String>
O exemplo BasicTable/HomeScreen.cs demonstra como usar essas classes para exibir um ListView
em apenas algumas linhas de código:
[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);
}
}
Manipulando cliques em linhas
Normalmente, um ListView
também permitirá que o usuário toque em uma linha para executar alguma ação (como tocar uma música, chamar um contato ou mostrar outra tela). Para responder aos toques do usuário é necessário que haja mais um método implementado no ListActivity
– OnListItemClick
– como este:
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();
}
Agora o usuário pode tocar em uma linha e um Toast
alerta aparecerá:
Implementando um ListAdapter
ArrayAdapter<string>
é ótimo por causa de sua simplicidade, mas é extremamente limitado. No entanto, muitas vezes você tem uma coleção de entidades comerciais, em vez de apenas cadeias de caracteres que deseja vincular.
Por exemplo, se seus dados consistirem em uma coleção de classes Employee, talvez você queira que a lista exiba apenas os nomes de cada funcionário. Para personalizar o comportamento de um ListView
para controlar quais dados são exibidos, você deve implementar uma subclasse de BaseAdapter
substituir os quatro itens a seguir:
Contagem – Para informar ao controle quantas linhas estão nos dados.
GetView – Para retornar um View para cada linha, preenchido com dados. Esse método tem um parâmetro para o
ListView
passar em uma linha existente e não utilizada para reutilização.GetItemId – Retorna um identificador de linha (normalmente o número da linha, embora possa ser qualquer valor longo que você desejar).
this[int] indexador – Para retornar os dados associados a um número de linha específico.
O código de exemplo em BasicTableAdapter/HomeScreenAdapter.cs demonstra como subclasse 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;
}
}
Usando um adaptador personalizado
Usar o adaptador personalizado é semelhante ao built-in ArrayAdapter
, passando a context
e o string[]
de valores para exibir:
ListAdapter = new HomeScreenAdapter(this, items);
Como este exemplo usa o mesmo layout de linha (SimpleListItem1
), o aplicativo resultante será idêntico ao exemplo anterior.
Reutilização do modo de exibição de linha
Neste exemplo, há apenas seis itens. Como a tela pode caber em oito, não é necessária a reutilização de linhas. Ao exibir centenas ou milhares de linhas, no entanto, seria um desperdício de memória criar centenas ou milhares de View
objetos quando apenas oito cabem na tela por vez. Para evitar essa situação, quando uma linha desaparece da tela, sua exibição é colocada em uma fila para reutilização. À medida que o usuário rola, as ListView
chamadas GetView
para solicitar que novos modos de exibição sejam exibidos – se disponíveis, ele passa uma exibição não utilizada no convertView
parâmetro. Se esse valor for null, seu código deverá criar uma nova instância de exibição, caso contrário, você poderá redefinir as propriedades desse objeto e reutilizá-lo.
O GetView
método deve seguir esse padrão para reutilizar modos de exibição de linha:
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;
}
As implementações personalizadas do adaptador devem sempre reutilizar o convertView
objeto antes de criar novos modos de exibição para garantir que eles não fiquem sem memória ao exibir listas longas.
Algumas implementações de adaptador (como o CursorAdapter
) não têm um GetView
método, mas exigem dois métodos NewView
diferentes e BindView
que impõem a reutilização de linhas separando as responsabilidades de GetView
em dois métodos. Há um CursorAdapter
exemplo mais adiante no documento.
Ativando a rolagem rápida
A Rolagem Rápida ajuda o usuário a percorrer longas listas, fornecendo um 'identificador' adicional que atua como uma barra de rolagem para acessar diretamente uma parte da lista. Esta captura de tela mostra a alça de rolagem rápida:
Fazer com que a alça de rolagem rápida apareça é tão simples quanto definir a FastScrollEnabled
propriedade como true
:
ListView.FastScrollEnabled = true;
Adicionando um índice de seção
Um índice de seção fornece feedback adicional para os usuários quando eles estão rolando rapidamente por uma longa lista – ele mostra para qual "seção" eles rolaram. Para fazer com que o índice de seção apareça, a subclasse Adapter deve implementar a ISectionIndexer
interface para fornecer o texto do índice, dependendo das linhas que estão sendo exibidas:
Para implementar ISectionIndexer
, você precisa adicionar três métodos a um adaptador:
GetSections – Fornece a lista completa de títulos de índice de seção que podem ser exibidos. Esse método requer uma matriz de objetos Java, portanto, o código precisa criar um
Java.Lang.Object[]
a partir de uma coleção .NET. Em nosso exemplo, ele retorna uma lista dos caracteres iniciais na lista comoJava.Lang.String
.GetPositionForSection – Retorna a posição da primeira linha para um determinado índice de seção.
GetSectionForPosition – Retorna o índice de seção a ser exibido para uma determinada linha.
O arquivo de exemplo SectionIndex/HomeScreenAdapter.cs
implementa esses métodos e algum código adicional no construtor. O construtor cria o índice de seção fazendo um loop por cada linha e extraindo o primeiro caractere do título (os itens já devem ser classificados para que isso funcione).
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]);
}
Com as estruturas de dados criadas, os ISectionIndexer
métodos são muito simples:
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;
}
Os títulos do índice de seções não precisam mapear 1:1 para suas seções reais. É por isso que o GetPositionForSection
método existe.
GetPositionForSection
dá-lhe a oportunidade de mapear quaisquer índices que estejam na sua lista de índices para quaisquer secções que estejam na sua vista de lista. Por exemplo, você pode ter um "z" em seu índice, mas talvez não tenha uma seção de tabela para cada letra, portanto, em vez de "z" mapear para 26, ele pode mapear para 25 ou 24, ou qualquer índice de seção "z" deve ser mapeado para.