Problema de clique de ListView personalizado em itens no Android

110

Portanto, tenho um objeto ListView personalizado. Os itens da lista têm duas visualizações de texto empilhadas uma sobre a outra, além de uma barra de progresso horizontal que desejo manter oculta até que realmente faça algo. À direita está uma caixa de seleção que desejo exibir apenas quando o usuário precisar baixar atualizações para seu (s) banco (s) de dados. Quando eu desabilito a caixa de seleção configurando a visibilidade para Visibility.GONE, posso clicar nos itens da lista. Quando a caixa de seleção está visível, não consigo clicar em nada na lista, exceto nas caixas de seleção. Fiz algumas pesquisas, mas não encontrei nada relevante para minha situação atual. Eu encontrei esta questãomas estou usando um ArrayAdapter substituído, pois estou usando ArrayLists para conter a lista de bancos de dados internamente. Eu preciso apenas obter a visualização LinearLayout e adicionar um onClickListener como Tom fez? Não tenho certeza.

Este é o XML do layout de linha do listview:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="6dip">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dip"
        android:layout_weight="1"
        android:layout_height="fill_parent">
        <TextView
            android:id="@+id/UpdateNameText"
            android:layout_width="wrap_content"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:textSize="18sp"
            android:gravity="center_vertical"
            />
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:id="@+id/UpdateStatusText"
            android:singleLine="true"
            android:ellipsize="marquee"
            />
        <ProgressBar android:id="@+id/UpdateProgress" 
                     android:layout_width="fill_parent" 
                     android:layout_height="wrap_content"
                     android:indeterminateOnly="false" 
                     android:progressDrawable="@android:drawable/progress_horizontal" 
                     android:indeterminateDrawable="@android:drawable/progress_indeterminate_horizontal" 
                     android:minHeight="10dip" 
                     android:maxHeight="10dip"                    
                     />
    </LinearLayout>
    <CheckBox android:text="" 
              android:id="@+id/UpdateCheckBox" 
              android:layout_width="wrap_content" 
              android:layout_height="wrap_content" 
              />
</LinearLayout>

E aqui está a classe que estende a ListActivity. Obviamente, ainda está em desenvolvimento, então perdoe as coisas que estão faltando ou podem ter ficado por aí:

public class UpdateActivity extends ListActivity {

    AccountManager lookupDb;
    boolean allSelected;
    UpdateListAdapter list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lookupDb = new AccountManager(this);
        lookupDb.loadUpdates();

        setContentView(R.layout.update);
        allSelected = false;

        list = new UpdateListAdapter(this, R.layout.update_row, lookupDb.getUpdateItems());
        setListAdapter(list);

        Button btnEnterRegCode = (Button)findViewById(R.id.btnUpdateRegister);
        btnEnterRegCode.setVisibility(View.GONE);

        Button btnSelectAll = (Button)findViewById(R.id.btnSelectAll);
        btnSelectAll.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                allSelected = !allSelected;

                for(int i=0; i < lookupDb.getUpdateItems().size(); i++) {
                    lookupDb.getUpdateItem(i).setSelected(!lookupDb.getUpdateItem(i).isSelected());
                }

                list.notifyDataSetChanged();
                // loop through each UpdateItem and set the selected attribute to the inverse 

            } // end onClick
        }); // end setOnClickListener

        Button btnUpdate = (Button)findViewById(R.id.btnUpdate);
        btnUpdate.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
            } // end onClick
        }); // end setOnClickListener

        lookupDb.close();
    } // end onCreate


    @Override
    protected void onDestroy() {
        super.onDestroy();

        for (UpdateItem item : lookupDb.getUpdateItems()) {
            item.getDatabase().close();        
        }
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        UpdateItem item = lookupDb.getUpdateItem(position);

        if (item != null) {
            item.setSelected(!item.isSelected());
            list.notifyDataSetChanged();
        }
    }

    private class UpdateListAdapter extends ArrayAdapter<UpdateItem> {
        private List<UpdateItem> items;

        public UpdateListAdapter(Context context, int textViewResourceId, List<UpdateItem> items) {
            super(context, textViewResourceId, items);
            this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View row = null;

            if (convertView == null) {
                LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = li.inflate(R.layout.update_row, null);
            } else {
                row = convertView;
            }

            UpdateItem item = items.get(position);

            if (item != null) {
                TextView upper = (TextView)row.findViewById(R.id.UpdateNameText);
                TextView lower = (TextView)row.findViewById(R.id.UpdateStatusText);
                CheckBox cb = (CheckBox)row.findViewById(R.id.UpdateCheckBox);

                upper.setText(item.getName());
                lower.setText(item.getStatusText());

                if (item.getStatusCode() == UpdateItem.UP_TO_DATE) {
                    cb.setVisibility(View.GONE);
                } else {
                    cb.setVisibility(View.VISIBLE);
                    cb.setChecked(item.isSelected());
                }

                ProgressBar pb = (ProgressBar)row.findViewById(R.id.UpdateProgress);
                pb.setVisibility(View.GONE);
            }
            return row;
        }

    } // end inner class UpdateListAdapter
}

editar: Ainda estou tendo esse problema. Estou trapaceando e adicionando manipuladores onClick aos textviews, mas parece extremamente estúpido que minha função onListItemClick () não esteja sendo chamada quando não estou clicando em minha caixa de seleção.

MattC
fonte

Respostas:

243

O problema é que o Android não permite que você selecione itens da lista que contenham elementos que possam ser focalizados. Modifiquei a caixa de seleção no item da lista para ter um atributo como este:

android:focusable="false"

Agora, os itens da minha lista que contêm caixas de seleção (funciona também para botões) são "selecionáveis" no sentido tradicional (eles acendem, você pode clicar em qualquer lugar no item da lista e o manipulador "onListItemClick" será acionado, etc).

EDIT: Como uma atualização, um comentarista mencionou "Apenas uma nota, depois de alterar a visibilidade do botão, tive que desativar o foco programaticamente novamente."

MattC
fonte
24
Parece que esta solução não funciona quando ImageButton é usado no item da lista. :(
Julian A.
1
Ok, mas e se eu quiser que minha caixa de seleção e minha visualização de lista sejam mutuamente exclusivas? Se eu selecionar o item ListView, não quero necessariamente verificar o checkBox. Isso é possível com esta solução?
Mike6679
@Mike Sim, esta solução ainda requer que a caixa de seleção seja clicada explicitamente para que haja interação. Ele apenas reativa a funcionalidade no ListView que misteriosamente é ignorada quando você tem elementos focalizáveis ​​no layout do item da lista.
MattC
2
Ótima resposta, obrigado, também funciona bem com ImageButtons. Apenas uma observação, depois de alterar a visibilidade do botão, tive que desabilitar programaticamente o foco novamente.
Ljdawson
1
também pode ser necessário definir android: clickable = "false"
Mina Samy
43

Caso você tenha ImageButton dentro do item da lista, você deve definir o descendantFocusabilityvalor para 'blocksDescendants' no elemento raiz do item da lista.

android:descendantFocusability="blocksDescendants"

E a focusableInTouchModebandeira truena ImageButtonvista.

android:focusableInTouchMode="true"
Micnoy
fonte
1
Isso funciona perfeitamente para listitem com botões de imagem. Mas isso pode funcionar, exceto ImageButton
raksja
12

Eu tive um problema semelhante ocorrendo e descobri que o CheckBox é bastante meticuloso em um ListView. O que acontece é que ele impõe sua vontade a todo o ListItem e meio que sobrescreve o onListItemClick. Você pode querer implementar um manipulador de cliques para isso e definir a propriedade text para a CheckBox também, em vez de usar TextViews.

Eu diria que olhe para este objeto View também, ele pode funcionar melhor do que o CheckBox

Ver Texto Verificado

Andrew Burgess
fonte
6
Descobri que, se você definir a configuração "focalizável" como false para os itens focalizáveis ​​na lista, poderá usar a função onListItemClick novamente.
MattC
2
No entanto, a caixa de seleção fica laranja como o item da lista. Alguém conhece uma solução?
erdomester
8

use esta linha na visão raiz do item da lista

android: descendantFocusability = "blocksDescendants"

Suresh Kumar
fonte