RecyclerView.ViewHolder - getLayoutPosition vs getAdapterPosition

88

Desde a nova versão da biblioteca de suporte (22.x), o getPosition()método da RecyclerView.ViewHolderclasse tornou-se obsoleto no lugar dos métodos mencionados no tópico. Eu realmente não entendo a diferença de ler os documentos. Alguém poderia explicar a diferença em termos leigos?

Eu tenho o seguinte caso de uso - eu dou a meu adaptador um List, e também quero poder associar informações extras para cada item da lista. Eu tenho um mapeamento de posição para extra, e o mapeamento está disponível para os proprietários para que eles possam buscar o extra para sua posição e fazer coisas com ele. No suporte, qual método devo usar?

O que acontece com as posições de titular quando os itens da lista nos índices 0 e 1 são trocados? O que os métodos retornam?

wujek
fonte

Respostas:

115

Esta é uma situação complicada, desculpe que os documentos não são suficientes.

Quando o conteúdo do adaptador muda (e você chama notify***()), o RecyclerView solicita um novo layout. A partir desse momento, até que o sistema de layout decida calcular um novo layout (<16 ms), a posição do layout e a posição do adaptador podem não corresponder porque o layout ainda não refletiu as alterações do adaptador.

Em seu caso de uso, como seus dados estão relacionados ao conteúdo do adaptador (e presumo que os dados sejam alterados ao mesmo tempo com as alterações do adaptador), você deve usar adapterPosition.

Porém, tenha cuidado, se você estiver chamando notifyDataSetChanged(), porque isso invalida tudo, o RecyclerView não sabe a posição do adaptador do ViewHolder até que o próximo layout seja calculado. Nesse caso, getAdapterPosition()irá retornar RecyclerView#NO_POSITION( -1).

Mas digamos que se você chamou notifyItemInserted(0), o getAdapterPosition()de ViewHolder que estava anteriormente na posição 0começará a retornar 1imediatamente. Portanto, enquanto você está despachando eventos de notificação granulares, você está sempre em bom estado (sabemos a posição do adaptador, embora o novo layout ainda não tenha sido calculado).

Outro exemplo, se você estiver fazendo algo no clique do usuário, se getAdapterPosition()retornar NO_POSITION, é melhor ignorar esse clique porque você não sabe o que o usuário clicou (a menos que você tenha algum outro mecanismo, por exemplo, ids estáveis ​​para pesquisar o item).

Editar para quando a posição do layout for boa

Vamos dizer que você está usando LinearLayoutManagere deseja acessar o ViewHolder acima do item clicado no momento. Nesse caso, você deve usar a posição do layout para obter o item acima.

mRecyclerView.findViewHolderForLayoutPosition(myViewHolder.getLayoutPosition() - 1)

Você deve usar a posição do layout porque corresponde ao que o usuário está vendo na tela.

yigit
fonte
1
Eu brinquei um pouco e descobri que o método getAdapterPosition () sempre retorna -1 para mim. Eu depurei e o motivo é que o código no método (final ViewParent parent = itemView.getParent (); if (! (Parent instanceof RecyclerView)) {return -1;} sempre entra no bloco if, ou seja, na visualização do reciclador não é o pai da minha visualização de célula. Como pode ser? Meu código para criar o titular é: return MyViewHolder (LayoutInflater.from (viewGroup.getContext ()). inflate (R.layout.test_list_item, viewGroup, false)); (Continua em outro comentário.)
wujek
Quando eu mudo o código para chamar inflate (R.layout.test_list_item, viewGroup, true); (observe o verdadeiro para 'anexar à raiz), o Android lança: java.lang.IllegalStateException: O filho especificado já tem um pai. Você deve primeiro chamar removeView () no pai da criança. Então, qual é a maneira correta de criar um suporte de visualização com uma visualização que esteja corretamente conectada à visualização do reciclador? Estranhamente, mesmo se getAdapterPosition () retornar -1 porque o pai é nulo, todo o resto funciona bem.
wujek
1
O parâmetro booleano no inflador de layout é "addToParent". Deve ser falso porque é responsabilidade do LayoutManager adicioná-lo. Acho que você está chamando getAdapterPosition no onBind, onde já passou a posição. Tecnicamente, o view holder representa aquela posição após o retorno de onBind. A propósito, atualizamos a posição getAdapter para retornar uma posição válida (se possível), mesmo que seja desanexada, ela será lançada em breve.
yigit de
Você está certo, estou chamando getAdapterPosition no onBind. O que devo fazer neste caso, em vez disso, preciso da posição para obter algumas informações que influenciam os estados de algumas visualizações. Chamar getLayoutPosition neste caso está certo?
wujek
5
Você deve usar o parâmetro de posição que é passado para o método onBind.
yigit,
2


Em ordem a discutir a diferença (s) de getAdapterPosition(), getLayoutPosition(), e também position; notaríamos os casos abaixo:

1. positionargumento no onBindViewHolder()método:

Podemos usar o positionpara vincular dados à visualização e não há problema em usar o positionargumento para fazer isso, mas não é permitido usar o positionargumento para lidar com cliques do usuário e, se você o usou, verá um aviso informando "não tratar positioncomo consertar e usarholder.getAdapterPosition() lugar ".

2getAdapterPosition() .:

Este método sempre consiste na posição atualizada do adaptador do holder. Isso significa que sempre que você clica em um item, você pergunta ao adaptador sobre eleposition . portanto, você obterá a posição mais recente deste item em termos da lógica do adaptador.

3 -getLayoutPosition() .:

Às vezes, é necessário encontrar o positionem termos do layout atualizado (o último layout passado que o usuário está vendo agora), por exemplo: Se o usuário solicitar o terceiro, positionele pode ver e você usar swipe/ dismisspara itens ou aplicar qualquer animação ou decorações para itens que será melhor usar em getLayoutPosition()vez de getAdapterPosition(), porque você sempre terá certeza de que está lidando com a posição dos itens em termos do último layout aprovado.


Para obter mais informações sobre isso; veja aqui . . .

elyar abad
fonte