o método getView do adaptador de listview personalizado sendo chamado várias vezes e em nenhuma ordem coerente

162

Eu tenho um adaptador de lista personalizado:

class ResultsListAdapter extends ArrayAdapter<RecordItem> {

no método 'getView' substituído, faço uma impressão para verificar qual é a posição e se é um convertView ou não:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        System.out.println("getView " + position + " " + convertView);

A saída disso (quando a lista é exibida pela primeira vez, nenhuma entrada do usuário ainda)

04-11 16:24:05.860: INFO/System.out(681): getView 0 null  
04-11 16:24:29.020: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43d415d8  
04-11 16:25:48.070: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.110: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.710: INFO/System.out(681): getView 0 android.widget.RelativeLayout@43d415d8  
04-11 16:25:50.251: INFO/System.out(681): getView 1 null  
04-11 16:26:01.300: INFO/System.out(681): getView 2 null  
04-11 16:26:02.020: INFO/System.out(681): getView 3 null  
04-11 16:28:28.091: INFO/System.out(681): getView 0 null  
04-11 16:37:46.180: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.091: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.730: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43cff8f0  

AFAIK, embora eu não tenha conseguido encontrá-lo explicitamente, getView () é chamado apenas para linhas visíveis. Como meu aplicativo inicia com quatro linhas visíveis, pelo menos os números de posição que variam de 0 a 3 fazem sentido. Mas o resto está uma bagunça:

  • Por que o getview é chamado para cada linha três vezes?
  • De onde vêm esses convertViews quando ainda não rolou?

Pesquisei um pouco e, sem obter uma boa resposta, notei que as pessoas estavam associando esse problema a problemas de layout. Portanto, no caso, aqui está o layout que contém a lista:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="fill_parent"
    android:layout_width="fill_parent" 
    android:orientation="vertical" >

    <TextView android:id="@+id/pageDetails"
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" />

    <ListView android:id="@+id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:drawSelectorOnTop="false" />

</LinearLayout>

e o layout de cada linha individual:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="108dp"    
android:padding="4dp">

<ImageView
    android:id="@+id/thumb"        
    android:layout_width="120dp"
    android:layout_height="fill_parent"        
    android:layout_alignParentTop="true"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_marginRight="8dp"        
    android:src="@drawable/loading" />

<TextView  
    android:id="@+id/price"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentBottom="true"       
    android:singleLine="true" />

<TextView  
    android:id="@+id/date"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true" 
    android:paddingRight="4dp"       
    android:singleLine="true" />

<TextView
    android:id="@+id/title"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="17dp" 
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:paddingRight="4dp"   
    android:layout_alignWithParentIfMissing="true"
    android:gravity="center" />

</RelativeLayout>

Obrigado pelo seu tempo

edzilhão
fonte

Respostas:

271

Isso não é um problema, não há absolutamente nenhuma garantia sobre a ordem em que getView()será chamado nem quantas vezes. No seu caso particular, você está fazendo a pior coisa possível com a ListView, dando a height=wrap_content. Isso força ListViewa medir algumas crianças fora do adaptador no momento do layout, para saber quão grande deve ser. É isso que fornece ListViewo que convertViewsvocê vê transmitido getView()antes mesmo de rolar.

Romain Guy
fonte
10
Não acho isso uma boa razão. Tudo bem se você quiser mudar, mas atualmente não está respondendo por que está fazendo esse comportamento?
Cdpnet
45
@cdpnet Ele disse por que ele estava fazendo isso, o seu ListViewtem uma altura de wrap_content, por isso não é certeza de altura, por isso, estabelece algumas crianças para fora como uma espécie de testador para ver o que vai caber no. Mude o seu ListViewpara fill_parentverificar, em seguida, seus logs as chamadas devem ser drasticamente reduzidas.
Blundell
4
O comportamento entre o ListView da era 2.3 e 4.x foi alterado significativamente. A conclusão é que, como o ListView foi projetado, suas células / list_items precisam ser vistas puras e precisam ser otimizadas para desenhar rapidamente. De acordo com a palestra do Google I / O no ListView, onde Romain apresentou, o getView pode ser chamado apenas para otimizar a renderização da exibição e descartar o resultado. Embora, na minha opinião, isso não seja tão amigável para desenvolvedores quanto o iOS UITableView, é como é.
Cameron Lowell Palmer
4
Muito obrigado! Não entendi por que o getView () é chamado com tanta frequência. I swithced wrap_content para fill_parent e agora meu aplicativo rápido é novamente :)
Julia Hexen
28
Deve haver um aviso ao definir ListViews como layout_height = wrap_content, pois causa muitos problemas de desempenho.
Nathanielwolf
53

Tente com match_parenta layout_heightpropriedade da exibição em lista. Evitará getView()ser chamado com tanta frequência.

Fred T
fonte
4
Note-se que Fred T especifica o layout_height da exibição de lista ... não a linha, que é o que eu tentei mais e mais
mblackwell8
8
Observe que fill_parentdeve ser substituído match_parentpelo nível 8 da API e superior.
Jason Axelson
Acho que o método getView () chamará várias vezes, definindo largura e altura como match_parent para o ListView apenas resolva o problema de desempenho.
Cuong Vo
45

Eu me livrei desse problema quando mudei layout_width e layout_height para match_parent (alterar apenas layout_height não ajudou).


Nota útil atente se você tiver itens aninhados. Você precisa alterar o "mais alto" para match_parent . Espero que ajude alguém.

Eugene Chumak
fonte
uma palavra amigo "impressionante", mas não sei por que wrap_content está criando problema. Isso resolveu meu problema.
Android Killer
@AndroidKiller quando você usa wrap_content, então ListViewnão sei o quanto lista de itens estão lá para tornar o conteúdo da Lista, é por isso que ListViewtenta criar tantas linhas como ele pensa são adequados para exibição, e quando você usa fill_parentou match_parent, ListViewpensa, tudo bem, minha altura é xe preciso do nnúmero de linhas para mostrar.
Adil Soomro
Muito obrigado ! Com esse comportamento, as imagens foram alteradas aleatoriamente por causa de várias chamadas ... Agora, tudo bem desde o início.
Chostakovitch 28/05
7

Não consigo responder à sua pergunta "Por que", mas definitivamente tenho uma solução para o problema dos itens irritantes do ListView repetidos " (se você tiver itens na coleção que sejam mais do que a altura da tela).

Como muitas pessoas acima mencionado, manter o android: layout_height propriedade do ListVew tag como fill_parent .

E sobre a função getView (), a solução é usar uma classe estática chamada ViewHolder . Confira este exemplo. Ele executa com êxito a tarefa de adicionar todos os itens em ur Array ou ArrayCollection.

Espero que isso ajude os amigos !!

Atenciosamente, Siddhant

Siddhant
fonte
Seja cauteloso ao usar o padrão ViewHolder na versão inicial do Android (pré-ICS), pois isso pode levar à exceção MemoryLeak. fique à vontade para usá-lo nas versões do ICS +.
Vahid Ghadiri
@ Siddhant obrigado Eu passo um dia para descobrir o motivo de repetir imagens no meu GridViewe mudar android:layout_heighte android:layout_widthnão funciona para mim. Mas o uso de ViewHolderimagens de repetição fixo para mim, fui através deste tutorial android-vogue.blogspot.com/2011/06/...
Nikolay Podolnyy
4

Perguntas: Por que o adaptador chama getView () várias vezes? Resp: À medida que o Listview é processado na rolagem, ele é atualizado com as próximas visualizações futuras, para as quais o adaptador precisa obter visualizações chamando getView ().

Perguntas: Por que as chamadas são menores se a largura e a altura do listview estiverem definidas como fill_parent? Resp: Como o inflator tem o tamanho fixo para a área da tela para lista, ele calcula uma vez para renderizar as visualizações na tela.

Espero que isso resolva sua consulta.

jitain sharma
fonte
Ótima explicação. Mas match_parent pode ser uma escolha melhor.
Fattie
Sim @JoeBlow, é recomendável usar match_parent em vez de fill_parent a partir da API 2.4 / 3.0
jitain sharma
4

Eu estava tendo o mesmo problema com a lista suspensa em um AutoCompleteTextView. Eu estava lutando com o problema por dois dias até chegar aqui e você me mostrar a solução.

Se eu escrever dropDownHeight = "match_parent", o problema será corrigido. Agora, o problema está relacionado à interface do usuário (quando você tem um item, o menu suspenso é muito grande), mas o problema de várias chamadas (muito mais importante) é corrigido.

Obrigado!!

Pau Arlandis Martinez
fonte
2

"Por que o getview é chamado para cada linha três vezes?" Como o getView é chamado quando você rola na lista e, para dizer melhor, é chamado quando a posição de uma exibição da sua lista é alterada!

Ungureanu Liviu
fonte
ok, mas a vista não mudou. há quatro vistas de linhas na tela quando a atividade começa, e sem rolagem ou qualquer entrada do usuário em tudo que eu vejo três chamadas GetView para cada linha ...
edzillion
Esta resposta está errada. A resposta correta é que o uso de ** wrap_content ** causa um grande número de chamadas ao getView.
Fattie
2

Estou tendo o mesmo problema. Se eu tiver a altura definida como fill_parent, recebo "geralmente" duas chamadas por linha. Mas, se eu definir a altura do meu ListView como valor exato, digamos 300dp, receberei exatamente uma chamada GetView por linha.

Portanto, parece-me que a única maneira é determinar primeiro a altura da tela e, em seguida, definir programaticamente a altura da lista com esse valor. Eu não gosto disso Espero que exista uma maneira melhor.

bobetko
fonte
1
Obrigado por isso. É engraçado - essa questão está adormecida há meses agora, apenas para que alguns comentários apareçam nas últimas semanas. Não estou mais fazendo esse projeto, mas, como tenho tempo, vou tentar fazer isso e voltar aqui para confirmar. obrigado novamente.
edzillion
Mesmo aqui, recebo duas chamadas por linha. Linhas 0,1,2,3 e, em seguida, 100ms depois, outro 0,1,2,3 - que é um pé no saco quando recolha de estatísticas sobre quais linhas são vistos
alguém em algum lugar
2

Para todos vocês que ainda (Depois de definir o heightda ListViewa match_parent) está preso (como eu era):

Você também deve definir o heightlayout pai como match_parent.

Veja o exemplo abaixo. O LinearLayouté o pai aqui:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/something" />

        <ListView
            android:id="@+id/friendsList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
B001 ᛦ
fonte
1

Talvez isso seja tarde, mas se você estiver usando, layout_weightlembre-se de sempre definirlayout_width="0dp"

Kuti Gbolahan
fonte
0

Eu fiz esta solução, talvez não seja a melhor, mas faz o trabalho ...

//initialize control string
private String control_input = " ";

então =

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View gridview = convertView;

    // change input_array for the array you use
    if (!control_input.equals(input_array[position])) {
        control_input = input_array[position];

        //do your magic

    } 

    return gridview;
}

espero que ajude!

yago
fonte