Android: acesse visualizações filhas em um ListView

108

Preciso descobrir a posição de pixel de um elemento em uma lista que foi exibida usando um ListView. Parece que eu deveria pegar um dos TextView e então usar getTop(), mas não consigo descobrir como obter uma visão secundária de um ListView.

Atualização: Os filhos de ViewGroupnão correspondem 1 a 1 com os itens da lista, para a ListView. Em vez disso, os ViewGroupfilhos de correspondem apenas às visualizações que estão visíveis no momento. Portanto, getChildAt()opera em um índice que é interno ao ViewGroupe não tem necessariamente nada a ver com a posição na lista que o ListViewusa.

laca
fonte

Respostas:

215

Veja: Android ListView: obtenha o índice de dados do item visível e combine com parte da resposta de Feet acima, pode fornecer algo como:

int wantedPosition = 10; // Whatever position you're looking for
int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount(); // This is the same as child #0
int wantedChild = wantedPosition - firstPosition;
// Say, first visible position is 8, you want position 10, wantedChild will now be 2
// So that means your view is child #2 in the ViewGroup:
if (wantedChild < 0 || wantedChild >= listView.getChildCount()) {
  Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen.");
  return;
}
// Could also check if wantedPosition is between listView.getFirstVisiblePosition() and listView.getLastVisiblePosition() instead.
View wantedView = listView.getChildAt(wantedChild);

O benefício é que você não está iterando nos filhos do ListView, o que poderia prejudicar o desempenho.

Joe
fonte
10
Ótima resposta, mas não funciona bem quando você tem visualizações de cabeçalho em sua lista. A tarefa de firstPositiondeve ser int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount();consertar isso.
pospi
@pospi: obrigado, bom argumento! Eu atualizei minha resposta para explicar isso.
Joe,
Boa resposta e funciona na maioria dos casos, mas não entendo por que você acredita que as visualizações filhas aparecem na ordem em que estão na matriz de visualizações filhas. Visto que as visualizações podem ser reutilizadas, como podemos ter certeza de que a primeira visualização não representa a última visualização visível?
Victor Denisov
2
@VictorDenisov: isso só pode acontecer no thread de IU; portanto, o encadeamento da interface do usuário será bloqueado enquanto este código estiver em execução, portanto, as visualizações filho são estacionárias e não serão modificadas. ListViewjá está manipulando a "movimentação" das visualizações filhas após reciclar programas antigos convertView, etc., portanto, você pode ter certeza de que ListView.getChildAt(0) é de fato a primeira visualização anexada do adaptador. Pode não estar totalmente visível (e pode nem mesmo ser visível, dependendo ListViewdo limite de "visibilidade" de antes de reciclar uma visualização que considera "rolada para fora da visualização")
Joe
1
@Matheus não é assim que o Android ListView funciona. Se o item da lista estiver fora da tela, sua exibição já foi reciclada e reutilizada para um dos outros itens da lista que estão na tela. Você deve repensar como está usando ListView se isso for um requisito.
Joe
17

Este código é mais fácil de usar:

 View rowView = listView.getChildAt(viewIndex);//The item number in the List View
    if(rowView != null)
        {
           // Your code here
        }
Kalimah
fonte
9
nem sempre funciona. getChildAt conta a partir da primeira linha visível, não a partir do topo dos dados.
SteelBytes
13
O argumento ViewID é confuso. É um índice (ou posição). O ID da vista é um número inteiro completamente arbitrário gerado pela ferramenta aapt.
Johan Pelgrim
6

Uma pesquisa rápida dos documentos da classe ListView revelou os métodos getChildCount () e getChildAt () herdados de ViewGroup. Você pode iterar por meio deles usando isso? Não tenho certeza, mas vale a pena tentar.

Encontrei aqui

Pés
fonte
1
Eu tentei isso, para fazer seleções. Geralmente funciona, mas às vezes ocorrem erros off-by-one e não é confiável. Eu não recomendaria.
Timmmm
Obrigado Timmmm, na verdade eu não tinha experimentado, apenas encontrei nos documentos.
Pés
5
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
        View v;
        int count = parent.getChildCount();
        v = parent.getChildAt(position);
        parent.requestChildFocus(v, view);
        v.setBackground(res.getDrawable(R.drawable.transparent_button));
        for (int i = 0; i < count; i++) {
            if (i != position) {
                v = parent.getChildAt(i);
                v.setBackground(res.getDrawable(R.drawable.not_clicked));
            }
        }
    }
});

Basicamente, crie dois Drawables - um transparente e outro da cor desejada. Solicite o foco na posição clicada ( int positionconforme definido) e altere a cor da referida linha. Em seguida, percorra o pai ListViewe altere todas as outras linhas de acordo. Isso contabiliza quando um usuário clica listviewvárias vezes. Isso é feito com um layout personalizado para cada linha no ListView. (Muito simples, basta criar um novo arquivo de layout com um TextView- não defina focalizável ou clicável!).

Nenhum adaptador personalizado necessário - use ArrayAdapter

Wes
fonte
getChildAt é relativo aos itens rolados atuais. Isso deixará de funcionar se você rolar a lista um pouco.
nurettin
4
int position = 0;
listview.setItemChecked(position, true);
View wantedView = adapter.getView(position, null, listview);
Milaaaad
fonte
1
Elabore
-9

Isso pressupõe que você conhece a posição do elemento no ListView:

  View element = listView.getListAdapter().getView(position, null, null);

Em seguida, você deve poder chamar getLeft () e getTop () para determinar os elementos na posição da tela.

jasonhudgins
fonte
43
getView()é chamado internamente pelo ListView, ao preencher a lista. Você não deve usá-lo para obter a visualização nessa posição na lista, pois chamar getView()com null para convertViewfaz com que o adaptador aumente uma nova visualização do recurso de layout do adaptador (não obtém a visualização que já está sendo exibida).
Joe de
1
A posição uniforme será uma posição na tela visual, não para a posição da fonte de dados do adaptador.
Vikas
@jasonhudgins Este é o método que venho usando, achei que é o melhor, mas quando tento fazer uma barra de progresso na visualização infantil invisível, mas em vez disso, todas as barras de progresso no listvew ficam invisíveis ........ tudo em todas as respostas aceitas funcionaram
Edijae Crusar