Personalizando a aparência de um ListView com o Xamarin.Android
A aparência de um ListView é ditada pelo layout das linhas que estão sendo exibidas. Para alterar a aparência de um ListView
, use um layout de linha diferente.
Exibições de linha internas
Há doze exibições internas que podem ser referenciadas usando Android.Resource.Layout:
TestListItem – Linha única de texto com formatação mínima.
SimpleListItem1 – Linha única de texto.
SimpleListItem2 – Duas linhas de texto.
SimpleSelectableListItem – Linha única de texto que dá suporte à seleção de um ou vários itens (adicionada no nível 11 da API).
SimpleListItemActivated1 – Semelhante a SimpleListItem1, mas a cor da tela de fundo indica quando uma linha é selecionada (adicionada no nível 11 da API).
SimpleListItemActivated2 – Semelhante a SimpleListItem2, mas a cor da tela de fundo indica quando uma linha é selecionada (adicionada no nível 11 da API).
SimpleListItemChecked – Exibe marcas de seleção para indicar a seleção.
SimpleListItemMultipleChoice – Exibe caixas de seleção para indicar a seleção de múltipla escolha.
SimpleListItemSingleChoice – Exibe botões de opção para indicar a seleção mutuamente exclusiva.
TwoLineListItem – Duas linhas de texto.
ActivityListItem – Linha única de texto com uma imagem.
SimpleExpandableListItem – Agrupa linhas por categorias e cada grupo pode ser expandido ou recolhido.
Cada modo de exibição de linha interno tem um estilo interno associado a ele. Essas capturas de tela mostram como cada exibição aparece:
O arquivo de exemplo BuiltInViews/HomeScreenAdapter.cs (na solução BuiltInViews ) contém o código para produzir as telas de item de lista não expansíveis. A exibição é definida no GetView
método assim:
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
As propriedades da exibição podem ser definidas referenciando os identificadores de Text1
controle padrão eIcon
, Text2
em Android.Resource.Id
(não defina propriedades que a exibição não contém ou uma exceção será lançada):
view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = item.Heading;
view.FindViewById<TextView>(Android.Resource.Id.Text2).Text = item.SubHeading;
view.FindViewById<ImageView>(Android.Resource.Id.Icon).SetImageResource(item.ImageResourceId); // only use with ActivityListItem
O arquivo de exemplo BuiltInExpandableViews/ExpandableScreenAdapter.cs (na solução BuiltInViews ) contém o código para produzir a tela SimpleExpandableListItem. A visualização de GetGroupView
grupo é definida no método assim:
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleExpandableListItem1, null);
A visualização filha é definida no GetChildView
método assim:
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleExpandableListItem2, null);
As propriedades da exibição de grupo e da exibição secundária podem ser definidas referenciando os identificadores padrão Text1
e Text2
de controle, conforme mostrado acima. A captura de tela SimpleExpandableListItem (mostrada acima) fornece um exemplo de uma exibição de grupo de uma linha (SimpleExpandableListItem1) e uma exibição filho de duas linhas (SimpleExpandableListItem2). Como alternativa, o modo de exibição de grupo pode ser configurado para duas linhas (SimpleExpandableListItem2) e o modo de exibição filho pode ser configurado para uma linha (SimpleExpandableListItem1), ou o modo de exibição de grupo e o modo de exibição filho podem ter o mesmo número de linhas.
Acessórios
As linhas podem ter acessórios adicionados à direita da exibição para indicar o estado da seleção:
SimpleListItemChecked – Cria uma lista de seleção única com uma marca de seleção como indicador.
SimpleListItemSingleChoice – Cria listas do tipo botão de opção em que apenas uma opção é possível.
SimpleListItemMultipleChoice – Cria listas do tipo caixa de seleção em que várias opções são possíveis.
Os acessórios acima mencionados são ilustrados nas telas a seguir, em sua respectiva ordem:
Para exibir um desses acessórios, passe o ID de recurso de layout necessário para o adaptador e, em seguida, defina manualmente o estado de seleção para as linhas necessárias. Esta linha de código mostra como criar e atribuir um Adapter
usando um destes layouts:
ListAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleListItemChecked, items);
O ListView
próprio suporta diferentes modos de seleção, independentemente do acessório que está sendo exibido. Para evitar confusão, use Single
o modo de seleção com SingleChoice
acessórios e o Checked
modo ou Multiple
com o MultipleChoice
estilo. O modo de seleção é controlado pela ChoiceMode
propriedade do ListView
.
Manipulando o nível da API
Versões anteriores do Xamarin.Android implementaram enumerações como propriedades inteiras. A versão mais recente introduziu tipos de enumeração .NET adequados, o que torna muito mais fácil descobrir as opções potenciais.
Dependendo de qual nível de API você está direcionando, ChoiceMode
é um número inteiro ou uma enumeração. O arquivo de exemplo AccessoryViews/HomeScreen.cs tem um bloco comentado se você quiser direcionar a API do Gingerbread:
// For targeting Gingerbread the ChoiceMode is an int, otherwise it is an
// enumeration.
lv.ChoiceMode = Android.Widget.ChoiceMode.Single; // 1
//lv.ChoiceMode = Android.Widget.ChoiceMode.Multiple; // 2
//lv.ChoiceMode = Android.Widget.ChoiceMode.None; // 0
// Use this block if targeting Gingerbread or lower
/*
lv.ChoiceMode = 1; // Single
//lv.ChoiceMode = 0; // none
//lv.ChoiceMode = 2; // Multiple
//lv.ChoiceMode = 3; // MultipleModal
*/
Selecionando itens programaticamente
A configuração manual de quais itens são 'selecionados' é feita com o SetItemChecked
método (pode ser chamado várias vezes para seleção múltipla):
// Set the initially checked row ("Fruits")
lv.SetItemChecked(1, true);
O código também precisa detectar seleções únicas de forma diferente de várias seleções. Para determinar qual linha foi selecionada no Single
modo, use a CheckedItemPosition
propriedade integer:
FindViewById<ListView>(Android.Resource.Id.List).CheckedItemPosition
Para determinar quais linhas foram selecionadas no Multiple
modo, você precisa percorrer o CheckedItemPositions
SparseBooleanArray
loop . Uma matriz esparsa é como um dicionário que contém apenas entradas em que o valor foi alterado, portanto, você deve percorrer toda a matriz procurando true
valores para saber o que foi selecionado na lista, conforme ilustrado no seguinte trecho de código:
var sparseArray = FindViewById<ListView>(Android.Resource.Id.List).CheckedItemPositions;
for (var i = 0; i < sparseArray.Size(); i++ )
{
Console.Write(sparseArray.KeyAt(i) + "=" + sparseArray.ValueAt(i) + ",");
}
Console.WriteLine();
Criando layouts de linha personalizados
As quatro exibições de linha internas são muito simples. Para exibir layouts mais complexos (como uma lista de e-mails, tweets ou informações de contato), é necessária uma exibição personalizada. As exibições personalizadas geralmente são declaradas como arquivos AXML no diretório Resources/Layout e, em seguida, carregadas usando sua ID de recurso por um adaptador personalizado. O modo de exibição pode conter qualquer número de classes de exibição (como TextViews, ImageViews e outros controles) com cores, fontes e layout personalizados.
Este exemplo difere dos exemplos anteriores de várias maneiras:
Herda de
Activity
, nãoListActivity
. Você pode personalizar linhas para qualquerListView
, no entanto, outros controles também podem ser incluídos em umActivity
layout (como um título, botões ou outros elementos da interface do usuário). Este exemplo adiciona um título acima doListView
para ilustrar.Requer um arquivo de layout AXML para a tela; Nos exemplos anteriores, o
ListActivity
não requer um arquivo de layout. Esse AXML contém uma declaração deListView
controle.Requer um arquivo de layout AXML para renderizar cada linha. Esse arquivo AXML contém os controles de texto e imagem com configurações personalizadas de fonte e cor.
Usa um arquivo XML de seletor personalizado opcional para definir a aparência da linha quando ela é selecionada.
A
Adapter
implementação retorna um layout personalizado daGetView
substituição.ItemClick
deve ser declarado de forma diferente (um manipulador de eventos é anexado emListView.ItemClick
vez de uma substituiçãoOnListItemClick
emListActivity
).
Essas alterações são detalhadas abaixo, começando com a criação da visualização da atividade e da visualização de linha personalizada e, em seguida, abrangendo as modificações no Adaptador e na Atividade para renderizá-las.
Adicionando um ListView a um layout de atividade
Como HomeScreen
não herda ListActivity
mais dele, não tem uma exibição padrão, um arquivo AXML de layout deve ser criado para a exibição da tela inicial. Para este exemplo, a exibição terá um título (usando um TextView
) e um ListView
para exibir dados. O layout é definido no arquivo Resources/Layout/HomeScreen.axml que é mostrado aqui:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/Heading"
android:text="Vegetable Groups"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:textSize="30dp"
android:textColor="#FF267F00"
android:textStyle="bold"
android:padding="5dp"
/>
<ListView android:id="@+id/List"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#FFDAFF7F"
/>
</LinearLayout>
A vantagem de usar um Activity
com um layout personalizado (em vez de um ListActivity
) está na capacidade de adicionar controles adicionais à tela, como o título TextView
neste exemplo.
Criando um layout de linha personalizado
Outro arquivo de layout AXML é necessário para conter o layout personalizado para cada linha que aparecerá no modo de exibição de lista. Neste exemplo, a linha terá um plano de fundo verde, texto marrom e imagem alinhada à direita. A marcação XML do Android para declarar esse layout é descrita em Resources/Layout/CustomView.axml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FFDAFF7F"
android:padding="8dp">
<LinearLayout android:id="@+id/Text"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<TextView
android:id="@+id/Text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic"
/>
<TextView
android:id="@+id/Text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#FF267F00"
android:paddingLeft="100dip"
/>
</LinearLayout>
<ImageView
android:id="@+id/Image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="5dp"
android:src="@drawable/icon"
android:layout_alignParentRight="true" />
</RelativeLayout >
Embora um layout de linha personalizado possa conter muitos controles diferentes, o desempenho de rolagem pode ser afetado por designs complexos e pelo uso de imagens (especialmente se elas precisarem ser carregadas pela rede). Consulte o artigo do Google para obter mais informações sobre como resolver problemas de desempenho de rolagem.
Referenciando uma exibição de linha personalizada
A implementação do exemplo de adaptador personalizado está em HomeScreenAdapter.cs
. O método de chave é GetView
onde ele carrega o AXML personalizado usando a ID Resource.Layout.CustomView
do recurso e, em seguida, define propriedades em cada um dos controles na exibição antes de retorná-lo. A classe de adaptador completa é mostrada:
public class HomeScreenAdapter : BaseAdapter<TableItem> {
List<TableItem> items;
Activity context;
public HomeScreenAdapter(Activity context, List<TableItem> items)
: base()
{
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override TableItem this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
View view = convertView;
if (view == null) // no view to re-use, create new
view = context.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
view.FindViewById<TextView>(Resource.Id.Text1).Text = item.Heading;
view.FindViewById<TextView>(Resource.Id.Text2).Text = item.SubHeading;
view.FindViewById<ImageView>(Resource.Id.Image).SetImageResource(item.ImageResourceId);
return view;
}
}
Referenciando o ListView personalizado na atividade
Como a HomeScreen
classe agora herda de Activity
, um ListView
campo é declarado na classe para conter uma referência ao controle declarado no AXML:
ListView listView;
Em seguida, a classe deve carregar o AXML de layout personalizado da atividade usando o SetContentView
método. Em seguida, ele pode encontrar o ListView
controle no layout, criar e atribuir o adaptador e atribuir o manipulador de cliques. O código do método OnCreate é mostrado aqui:
SetContentView(Resource.Layout.HomeScreen); // loads the HomeScreen.axml as this activity's view
listView = FindViewById<ListView>(Resource.Id.List); // get reference to the ListView in the layout
// populate the listview with data
listView.Adapter = new HomeScreenAdapter(this, tableItems);
listView.ItemClick += OnListItemClick; // to be defined
Finalmente, o ItemClick
manipulador deve ser definido; neste caso, ele apenas exibe uma Toast
mensagem:
void OnListItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
var listView = sender as ListView;
var t = tableItems[e.Position];
Android.Widget.Toast.MakeText(this, t.Heading, Android.Widget.ToastLength.Short).Show();
}
A tela resultante tem esta aparência:
Personalizando a cor do seletor de linha
Quando uma linha é tocada, ela deve ser destacada para feedback do usuário. Quando um modo de exibição personalizado especifica como cor de plano de fundo como CustomView.axml , ele também substitui o realce de seleção. Essa linha de código em CustomView.axml define o plano de fundo como verde claro, mas também significa que não há nenhum indicador visual quando a linha é tocada:
android:background="#FFDAFF7F"
Para reativar o comportamento de realce e também para personalizar a cor usada, defina o atributo de plano de fundo como um seletor personalizado. O seletor declarará a cor de fundo padrão e a cor de realce. O arquivo Resources/Drawable/CustomSelector.xml contém a seguinte declaração:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false"
android:state_selected="false"
android:drawable="@color/cellback" />
<item android:state_pressed="true" >
<shape>
<gradient
android:startColor="#E77A26"
android:endColor="#E77A26"
android:angle="270" />
</shape>
</item>
<item android:state_selected="true"
android:state_pressed="false"
android:drawable="@color/cellback" />
</selector>
Para fazer referência ao seletor personalizado, altere o atributo de plano de fundo em CustomView.axml para:
android:background="@drawable/CustomSelector"
Uma linha selecionada e a mensagem correspondente Toast
agora têm esta aparência:
Evitando cintilação em layouts personalizados
O Android tenta melhorar o desempenho da rolagem armazenando em cache as informações de ListView
layout. Se você tiver listas de dados de rolagem longas, também deverá definir a android:cacheColorHint
ListView
propriedade na declaração na definição AXML da atividade (com o mesmo valor de cor que o plano de fundo do layout de linha personalizado). A não inclusão dessa dica pode resultar em uma 'cintilação' à medida que o usuário percorre uma lista com cores de plano de fundo de linha personalizadas.