Compartilhar via


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:

Capturas de tela de TestListItem, SimpleSelectableListItem, SimpleListitem1 e SimpleListItem2

Capturas de tela de SimpleListItemActivated1, SimpleListItemActivated2, SimpleListItemCheck e SimpleListItemMultipleChecked

Capturas de tela de SimpleListItemSingleChoice, TwoLineListItem, ActivityListItem e SimpleExpandableListItem

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 Text1controle 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:

Capturas de tela de SimpleListItemChecked, SimpleListItemSingleChoice e SimpleListItemMultipleChoice com acessórios

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 SparseBooleanArrayloop . 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ão ListActivity . Você pode personalizar linhas para qualquer ListView , no entanto, outros controles também podem ser incluídos em um Activity layout (como um título, botões ou outros elementos da interface do usuário). Este exemplo adiciona um título acima do ListView 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 de ListView 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 da GetView substituição.

  • ItemClick deve ser declarado de forma diferente (um manipulador de eventos é anexado em ListView.ItemClick vez de uma substituição OnListItemClick em ListActivity).

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.CustomViewdo 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:

Captura de tela do CustomRowView resultante

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:

Uma linha selecionada em laranja, com a mensagem Toast exibindo o nome da linha selecionada

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.