ListView addHeaderView faz com que a posição aumente em um?


97

Abaixo está um trecho de código com um ListView. Eu adicionei um emptyView e um headerView. Adicionar headerView faz com que a posição em onItemClick seja aumentada em um.

Portanto, sem headerView o primeiro elemento da lista teria a posição 0, com headerView a posição do primeiro elemento da lista seria 1!

Isso causa erros em meu adaptador, por exemplo, ao chamar getItem () e usar alguns outros métodos, veja abaixo. Coisa estranha: no método getView () do adaptador, o primeiro elemento da lista é solicitado com a posição 0, mesmo se headerView for adicionado !!

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ListView list = (ListView) viewSwitcher.findViewById(R.id.list);
    View emptyView = viewSwitcher.findViewById(R.id.empty);
    list.setEmptyView(emptyView);

    View sectionHeading = inflater.inflate(R.layout.heading, list, false);
    TextView sectionHeadingTextView = (TextView) sectionHeading.findViewById(R.id.headingText);
    sectionHeadingTextView.setText(headerText);
    list.addHeaderView(sectionHeading);

    list.setAdapter(listAdapter);

    list.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            listAdapter.getItem(position);
            //Do something with it
        }
    });
}

Alguns métodos do adaptador:

@Override
public int getViewTypeCount() {
    return TYPE_COUNT;
}

@Override
public int getItemViewType(int position) {
    return (position < items.size()) ? ITEM_NORMAL : ITEM_ADVANCED;
}

    @Override
public Product getItem(int position) {
    return items.get(position);
}
@Override
public boolean areAllItemsEnabled() {
    return false;
}

@Override
public boolean isEnabled(int position) {
    return (position < items.size());
}

Este é o comportamento normal ao adicionar um headerView ?? E como superar os problemas do meu adaptador?


1
Sua pergunta teve a resposta para minha pergunta. +1
Shashank Degloorkar

Respostas:


113

Acabei de me deparar com esse problema e a melhor maneira parece usar o em ListView.getItemAtPosition(position)vez de ListAdapter.getItem(position)como a ListViewversão representa os cabeçalhos, ou seja: -

Em vez disso:

myListView.getItemAtPosition(position)

1
Tão certo! onItemClickdá a você parentum motivo. Você pode usarparent.getItemAtPosition(position)
Brais Gabin

3
Isso realmente não funcionou para mim, enquanto usar a solução de adaptador @whuandroid decorado / invólucro sim.
Dori

3
também você deve lançar o item neste caso.
njzk2

Não funciona para mim, mesmo se eu transmitir pai para ListView. Eu só preciso descartar a posição == 0 ...
PawelP

Nao funciona para mim. AdapterViewtambém apenas passa para o listAdapter adicionado. return (adapter == null || position < 0) ? null : adapter.getItem(position);
philipp

31

Se você não se importa com o clique no cabeçalho, subtraia o número de visualizações do cabeçalho da posição para obter a posição do adaptador:

        listView.addHeaderView(inflater.inflate(
                R.layout.my_hdr_layout, null), null, false);
        listView.setAdapter(m_adapter);
        listView.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {
            position -= listView.getHeaderViewsCount();
            final MyObject object = m_adapter.getItem(position);

        }
    });

Esta deve ser a melhor resposta, não me importo com o cabeçalho, mas com o listitem.
Ninja

Acho que esta solução não funciona para o OS 7.0 mais recente
sha

27

Se você adicionar um cabeçalho a um, ListViewele se tornará o primeiro item da lista. Você pode compensar isso, substituindo o getCount()para retornar um item menos, mas garantir que você lidar com o getItem(), getItemId()e assim por diante como o cabeçalho seria o item na posição 0.


6
Substituir getCount () para retornar um item a menos não funciona, porque então eu perco o último item da lista. Se houver apenas 1 item na lista, getView () nunca será chamado. Além disso, o adaptador é desacoplado do listview e não sabe se houve um cabeçalho adicionado ao listview ou não.
d1rk de

2
No ListItemClick, defina position = position - 1 se houver um cabeçalho anexado, acho que foi isso que fiz quando enfrentei esse problema específico, não é muito legal, mas a única maneira que consigo pensar
akshaydashrath

3
Acho que a maneira mais fácil seria definir position = position- (número de headerviews adicionados) no OnItemClickListener como você sugeriu. Mudei meu adaptador para lidar com o headerview em si, portanto, não preciso chamar addHeaderView no listview. Embora o adaptador tenha ficado um pouco mais complexo.
d1rk

22
O "número de headerviews adicionados" pode ser obtido com:ListView.getHeaderViewsCount()
João

2
Não vejo como essa é a resposta certa. @Ian Warwick postou a correta, embora um pouco tarde, eu suponho.
sidon

14

Ao implementar AdapterView.OnItemClickListener, apenas subtraímos os cabeçalhos da posição.

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    position -= mListView.getHeaderViewsCount();
    // what ever else follows...
}

13

Não há necessidade de alterar nenhum código. Basta usar o código abaixo.

 list.setOnItemClickListener(new OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      parent.getAdapter().getItem(position);
         //Do something with it
     }
});

1
Isso geralmente precisará de fundição, pois o item que está sendo devolvido é apenas "Objeto".
desenvolvedor Android de

9

Não use o seu adaptador, use o adaptador 'decorado' da getAdapter.


+1 este IMO é o caminho a percorrer - o adaptador que você fornece é embalado com outro adaptador que leva em conta o deslocamento do índice quando você fornece um cabeçalho. Este código para isso é fornecido por @Ramesh em outro lugar nesta página (+1 para você também!)
Dori

5

Uma solução para isso é mapear o id para o índice. Basicamente, make id retornar o índice na lista e o listener de clique faria:

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            listAdapter.getItem((int) id);
}

3

Olá podemos resolver isso dessa forma primeiro você cria sua view de cabeçalho, depois injeta em sua lista e a última etapa não consegue o setOnClickListener. suas obras para mim. qualquer feedback bem-vindo

ViewGroup headerView = (ViewGroup) getLayoutInflater().inflate(R.layout.your_header, list,false);
list.addHeaderView(headerView);
headerView.setEnabled(false);
headerView.setOnClickListener(null);

É bem no final da lista de respostas, mas isso é muito útil. Se você não fizer essas alterações, sua visualização do cabeçalho permanecerá clicável, com efeitos de ondulação etc.
Scott Ferguson

0

Sem querer adicionar muitas novidades que @Ramesh não adicionou, mas algum contexto extra:

personList.setOnItemClickListener(new OnItemClickListener() {
    @SuppressWarnings("unchecked")
    public void onItemClick( AdapterView<?> parent, View view, int position, long duration) {
        Map<String, Object> person = (Map<String, Object>) parent.getAdapter().getItem(position);
        if (person != null){

            // If the header was clicked on a null  is returned

            OnPersonUiChangeListener listener = (OnPersonUiChangeListener) getActivity();
            listener.onSelectedPersonChanged(person);
        }
    }
});

0

Use getSelectedItemPosition () Retorne a posição do item atualmente selecionado dentro do conjunto de dados do adaptador. Este método não precisa se preocupar com o tamanho dos cabeçalhos ou rodapés.


0

Isso pode ajudar alguém, melhorando a resposta de Farid_z e Philipp, podemos aplicar corretamente o setItemChecked e setTitle com algo assim

        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
        position += 1;
        mDrawerList.setItemChecked(position, true);
        mDrawerList.setSelection(position);
        position -= 1;
        setTitle(mNavigationDrawerItemTitles[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.