Qual é a intenção dos métodos getItem e getItemId na classe Android BaseAdapter?

155

Estou curioso sobre o objetivo dos métodos getIteme getItemIdna classe Adapter no Android SDK.

A partir da descrição, parece que getItemdeve retornar os dados subjacentes. Então, se eu tiver uma matriz de nomes ["cat","dog","red"]e criar um adaptador ausando isso, a.getItem(1)retornarei "dog", correto? O que deve a.getItemId(1)retornar?

Se você usou esses métodos na prática, poderia dar um exemplo?

oneporter
fonte
16
+1 Excelente pergunta. Quero salientar que getItemId()em ArrayAdapter()sempre retorna -1comassert false : "TODO"; return -1;
rds

Respostas:

86

Eu vejo esses métodos como uma abordagem mais limpa para acessar os dados da minha lista. Em vez de acessar diretamente o objeto do meu adaptador através de algo como myListData.get(position)eu posso simplesmente chamar o adaptador como adapter.get(position).

O mesmo vale para getItemId. Normalmente, eu usaria esse método quando desejar executar alguma tarefa com base no ID exclusivo de um objeto na lista. Isso é especialmente útil ao trabalhar com um banco de dados. O retorno idpode ser uma referência a um objeto no banco de dados no qual eu poderia executar diferentes operações (atualização / exclusão / etc).

Portanto, em vez de acessar o ID a partir do objeto de dados brutos, como myListData.get(position).getId()você pode usar adapter.getItemId(position).

Um exemplo de onde eu senti que precisava usar esses métodos estava em um projeto usando o SeparatedListViewAdapter . Esse adaptador pode conter vários tipos diferentes de adaptadores, cada um representando dados de um tipo diferente (normalmente). Ao chamar getItem(position)o SeparatedListViewAdapter, o objeto retornado pode ser diferente dependendo de qual "seção" a posição é que você o envia.

Por exemplo, se você tinha 2 seções na sua lista (frutas e doces): Se você usou getItem(position)e positionpassou a ser em um item na fruta seção, você receberia um objeto diferente do que se solicitado getItem(position)com positionapontando para um item na doces seção. Você pode então retornar algum tipo de valor de ID constante no getItemId(position)qual representa que tipo de dados getItem(position)está retornando ou usar instanceofpara determinar qual objeto você possui.

Além do que mencionei, nunca senti que realmente precisava usar esses métodos

James
fonte
7
para adaptadores não relacionados a sql, o getItemId ainda terá um objetivo? Se sim, o que deve ser devolvido? posição?
desenvolvedor android
1
o objetivo ou uso do método depende principalmente do desenvolvedor e não está vinculado a um aplicativo orientado a banco de dados. use-o a seu favor para criar um código claro / legível / reutilizável.
james
1
Sim, eu acho. getView, getCount, getViewTypeCount, Etc são usados especificamente para mostrar corretamente seu UI listview. as outras funções simplesmente ajudam a criar implementar outras funcionalidades, tais como realização de novas acções ao clicar um item, etc., embora muitas vezes eu uso getItemdentrogetView
james
1
@NicolasZozol Claro - é seguro não implementar getItemId, basta retornar 0Lou nullnão usá-lo em qualquer lugar. Não vejo nenhuma razão óbvia para que um UUID seja mais valioso do que apenas algum longvalor para identificação. Modo desconectado? O que é isso?
james
1
@binnyb: Nicolas quis dizer que, com os UUIDs, ainda é possível criar IDs válidos (por exemplo, no seu dispositivo móvel), mesmo sem ter uma conexão de rede.
Levite
32

Bem, parece que essa pergunta poderia ser respondida de uma maneira mais simples e direta ... :-)

Simplificando, o Android permite anexar um longa qualquer ListViewitem, é simples assim. Quando o sistema notifica você sobre a seleção do usuário, você recebe três variáveis ​​de identificação para informar o que foi selecionado:

  • uma referência à própria visualização,
  • sua posição numérica na lista,
  • isso longvocê anexou aos elementos individuais.

Cabe a você decidir qual desses três é o mais fácil de lidar no seu caso particular, mas você tem todos os três para escolher o tempo todo. Pense nisso longcomo uma tag anexada automaticamente ao item, apenas que é ainda mais simples e fácil de ler.

O mal-entendido sobre o que geralmente faz decorre de uma simples convenção. Todos os adaptadores precisam fornecer um getItemId()mesmo que não usem essa terceira identificação. Portanto, por convenção, esses adaptadores (incluindo muitos exemplos no SDK ou em toda a Web) simplesmente retornam positionpor um único motivo: é sempre único. Ainda assim, se um adaptador retornar position, isso realmente significa que ele não deseja usar esse recurso, pois positionjá é conhecido.

Portanto, se você precisar retornar qualquer outro valor que achar conveniente, sinta-se à vontade para fazê-lo:

@Override
public long getItemId(int position) {
  return data.get(position).Id;
}
Gábor
fonte
1
Boa explicação para isso getItemId()... O que acontece quando / se esse método não é substituído no seu adaptador personalizado?
dentex
Sendo marcado como abstrato na classe base, você precisa. A menos que você substitua algo que substitui o adaptador original, é claro. Tente deixar de fora e, se o Eclipse reclamar, é necessário. :-)
Gábor
Obrigado. Eu sempre tive esse método comentado sem avisos. Eu tenho um CustomAdapter estende ArrayAdapter <CustomListItem> com getCount (), getItem (...) e getView (...), usando o "padrão de suporte". Apenas por curiosidade ...
dentex
Sim, você pode fazer isso porque o ArrayAdapter estende o BaseAdapter e já fornece sua própria implementação.
19414 Gábor
E com uma matriz simples, tudo bem. Mas considere outro caso, quando você deseja exibir itens de um banco de dados, por exemplo. Você provavelmente estenderá o BaseAdapter e poderá usar esse ID longo para armazenar a chave do banco de dados. Quando o usuário seleciona algo, você recupera diretamente a chave do registro selecionado por meio do argumento id . Você pode carregá-lo do banco de dados imediatamente, por exemplo. O único problema é que você precisa usar teclas numéricas porque o Android decidiu por um longo, em vez de algo mais amplo.
19414 Gábor
6

O getItemIdmétodo foi amplamente projetado para trabalhar com cursores suportados por bancos de dados SQLite. Ele retornará o campo de identificação do cursor subjacente para o item na posição 1.

No seu caso, não há um ID para o item na posição 1: estou assumindo que a implementação do ArrayAdapter retorne -1 ou 0.

EDIT: na verdade, apenas retorna a posição: neste caso 1.

Femi
fonte
2
Não, ele retorna -1. Aqui está a implementaçãoassert false : "TODO"; return -1;
rds
5
A partir do Android 4.1.1, ele retorna a posição: grepcode.com/file/repository.grepcode.com/java/ext/…
emmby em 21/08/2012
4

Gostaria de mencionar que após a implementação getIteme getItemIdvocê pode usar o ListView.getItemAtPosition e o ListView.getItemIdAtPosition para acessar diretamente os dados, em vez de passar pelo adaptador. Isso pode ser particularmente útil ao implementar um ouvinte onClick.

leo9r
fonte
3
Este é de facto extremamente útil se você tem um cabeçalho em seu listview e as posições passadas para o clique manipulador são desativados por uma
entropia
4

Se você implementar getItemIdcorretamente, pode ser muito útil.

Exemplo:

Você tem uma lista de álbuns:

class Album{
     String coverUrl;
     String title;
}

E você implementa getItemIdassim:

@Override
public long getItemId(int position){
    Album album = mListOfAlbums.get(position);
    return (album.coverUrl + album.title).hashcode();
}

Agora, o ID do seu item depende dos valores dos campos coverUrl e title e, se você alterar e ligar notifyDataSetChanged()para o adaptador, o adaptador chamará o método getItemId () de cada elemento e atualizará apenas os itens cujo ID foi alterado.

Isso é muito útil se você estiver executando algumas operações "pesadas" no seu getView().

BTW: se você quiser que isso funcione, você precisa garantir que seu hasStableIds()método retorne false;

Danylo Volokh
fonte
Esta é uma observação valiosa. Você pode fornecer alguns dados para apoiar esse mecanismo de atualização seletiva?
Jaime Agudo
Por que hasStableIds()retornar falso? Parece-me que o código hash calculado a partir da mesma string retornaria o mesmo valor toda vez, o que é um ID estável de acordo com os documentos .
Big McLargeHuge
considerar que o uso de hashCode pode retornar verdadeiro para duas cordas
htafoya
2

getItemou getItemIdhá poucos métodos projetados principalmente para anexar dados com itens da lista. No caso de getItem, você pode passar qualquer objeto que será anexado ao item na lista. Normalmente as pessoas retornam null. getItemIdé qualquer longvalor exclusivo que você pode anexar com o mesmo item na lista. As pessoas geralmente retornam a posição na lista.

Qual o uso. Bem, como esses valores estão vinculados ao item da lista, você pode extraí-los quando o usuário clicar no item. Esses valores são acessíveis através de AdapterViewmétodos.

// template class to create list item objects
class MyListItem{
    public String name;
    public long dbId;

    public MyListItem(String name, long dbId){
        this.name = name;
        this.dbId = dbId;
    }
}

///////////////////////////////////////////////////////////

// create ArrayList of MyListItem
ArrayList<MyListItem> myListItems = new ArrayList<MyListItem>(10);

// override BaseAdapter methods
@Override
public Object getItem(int position) {
    // return actual object <MyListItem>
    // which will be available with item in ListView
    return myListItems.get(position);
}

@Override
public long getItemId(int position) {
    // return id of database document object
    return myListItems.get(position).dbId;
}

///////////////////////////////////////////////////////////

// on list item click, get name and database document id
my_list_view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

        // extract item data
        MyListItem selectedItem = (MyListItem)parent.getItemAtPosition(position);      
        System.out.println("Your name is : " + selectedItem.name);

        // extract database ref id
        long dbId = id;

        // or you could also use
        long dbId = parent.getItemIdAtPosition(position);
    }
});
Uday Hiwarale
fonte