Como disparar onListItemClick em Listactivity com botões na lista?

89

Eu tenho um ListActivity simples que usa um ListAdapter personalizado para gerar os modos de exibição na lista. Normalmente, o ListAdapter apenas preencheria as visualizações com TextViews, mas agora quero colocar um botão lá também.

É meu conhecimento e experiência, entretanto, que colocar uma visualização focalizável no item da lista evita o disparo de onListItemClick () na ListActivity quando o item da lista é clicado. O botão ainda funciona normalmente dentro do item da lista, mas quando algo além do botão é pressionado, quero que onListItemClick seja acionado.

Como posso fazer isso funcionar?

CodeFusionMobile
fonte
5
sua solução com foco descendente é realmente útil, você deve adicioná-la como uma resposta e aceitá-la!
Maksym Gontar
@Max O motivo pelo qual não faço isso é porque é uma prática muito ruim, uma solução alternativa. Se eu alguma vez encontrasse uma solução saudável permanente, eu daria uma resposta (se me lembro que escrevi esta pergunta há um ano :))
CodeFusionMobile
Também gostaria de ver a solução alternativa que você tem. Tenho tentado definir o foco descendente e não consigo fazê-lo funcionar com botões. Também tenho tentado colocar um GridView (com ImageViews) na linha da lista e que tem problemas semelhantes.
MrPurpleStreak
A resposta de IMHO que dei é uma solução muito mais elegante para o problema do que aquela proposta por Ramps e Praveen. PSNão estou tentando reviver a pergunta esquecida aqui, mas vejo que você ainda não aceitou nenhuma resposta; D
Ewoks
@CodeFusionMobile Você poderia aceitar a resposta de Ewoks? A resposta com maior votação é falha porque desativa a animação onclick para o elemento ListView (onde fica azul). Isso economizaria algum tempo de outros desenvolvedores tentando a resposta principal, descobrindo que ela é falha e, em seguida, indo para o Ewoks.
1 '' de

Respostas:

99

como escrevi no comentário anterior, a solução é setFocusable (false) em ImageButton.

Existe uma solução ainda mais elegante para tentar adicionar o android:descendantFocusability="blocksDescendants" layout raiz do elemento da lista. Isso tornará os cliques emListItem possíveis e, separadamente, você pode lidar com cliques de botão ou ImageButton

Espero que ajude ;)

Felicidades

Ewoks
fonte
3
android:descendantFocusability="blocksDescendants"foi perfeito.
Matthew Mitchell
1
você, meu amigo, é meu herói pessoal. você deve ser o mais votado! isso é muito melhor para quase todos os casos!
OschtärEi
Esta deve ser a resposta aceita. Funcionou perfeitamente para mim. Parece que todas as visualizações no item da lista precisam ser desfocadas e este faz exatamente isso sem definir cada uma individualmente.
rushinge
1
Isso não parece funcionar para mim. Por layout de raiz, você quer dizer o layout de linha ou onde o listview é definido? De qualquer forma, tentei todas as combinações possíveis .. não está funcionando :(
GreenBee
"Layout raiz do elemento da lista", como escrevi .. Digamos que você tenha uma lista e layouts (chame-os de layout raiz do elemento da lista) que têm vários Button, ImageButton, TextVeiw ..
Ewoks
59

Espero poder ajudar aqui. Presumo que você tenha um layout personalizado para itens listView, e esse layout consiste em um botão e algumas outras visualizações - como TextView, ImageView ou qualquer outra. Agora você deseja ter um evento diferente disparado no clique do botão e um evento diferente disparado em todos os outros cliques.

Você pode conseguir isso sem usar onListItemClick () de sua ListActivity.
Aqui está o que você deve fazer:

Você está usando um layout personalizado, então provavelmente está substituindo o método getView () de seu adaptador personalizado. O truque é definir os ouvintes diferentes para o botão e diferentes para a exibição inteira (sua linha). Dê uma olhada no exemplo:

private class MyAdapter extends ArrayAdapter<String> implements OnClickListener {

    public MyAdapter(Context context, int resource, int textViewResourceId,
            List<String> objects) {
        super(context, resource, textViewResourceId, objects);
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        String text = getItem(position);
        if (null == convertView) {
            convertView = mInflater.inflate(R.layout.custom_row, null);
        }
        //take the Button and set listener. It will be invoked when you click the button.
        Button btn = (Button) convertView.findViewById(R.id.button);
        btn.setOnClickListener(this);
        //set the text... not important     
        TextView tv = (TextView) convertView.findViewById(R.id.text);
        tv.setText(text);
        //!!! and this is the most important part: you are settin listener for the whole row
        convertView.setOnClickListener(new OnItemClickListener(position));
        return convertView;
    }

    @Override
    public void onClick(View v) {
        Log.v(TAG, "Row button clicked");
    }
}

Sua classe OnItemClickListener pode ser declarada como aqui:

private class OnItemClickListener implements OnClickListener{       
    private int mPosition;
    OnItemClickListener(int position){
        mPosition = position;
    }
    @Override
    public void onClick(View arg0) {
        Log.v(TAG, "onItemClick at position" + mPosition);          
    }       
}

É claro que você provavelmente adicionará mais alguns parâmetros ao construtor OnItemClickListener.
E uma coisa importante - a implementação de getView mostrada acima é muito feia, normalmente você deve usar o padrão ViewHolder para evitar chamadas findViewById .. mas provavelmente você já sabe disso.
Meu arquivo custom_row.xml é RelativeLayout com Botão de id "botão", TextView de id "texto" e ImageView de id "imagem" - apenas para deixar as coisas claras.
Saudações!

Rampas
fonte
9
Minha solução acabou indo por outro caminho. Resolvi o problema definindo a propriedade descendente Focusability do layout de linha da lista para bloquear descendentes e de repente funcionou. Agora eu uso um simpleCursorAdapter com um viewbinder personalizado anexado a ele para fazer a configuração do evento e outras coisas sofisticadas associadas aos botões.
CodeFusionMobile de
3
Eu usei essa solução. No entanto, um aviso: se seu manipulador onClick fizer com que o conteúdo da lista mude, o que resultaria em getView sendo chamado E se seu widget tiver estado (como um ToggleButton ou CheckBox), E getView definir esse estado, você pode querer setOnClickListener (nulo) antes de alterar o estado em onView, para evitar obter um efeito de loop.
Pierre-Luc Paour
3
e a minha solução? Tenho a impressão de que é mais elegante e pode ser aplicado mais facilmente sempre que necessário ..
Ewoks
o primeiro comentário deve ser aceito como resposta, para que pessoas como eu não precisem de horas para encontrá-lo
keybee
Obrigado! Isso foi útil. Eu gosto disso como uma alternativa mais localizada para a abordagem focus: blockDescendants descrita em outros tópicos.
Yoav Shapira
18

Quando um ListView personalizado contém elementos focalizáveis, onListItemClick não funciona (acho que é o comportamento esperado). Basta remover o foco da visualização personalizada. Isso resolverá o problema:

Por exemplo:

public class ExtendedCheckBoxListView extends LinearLayout {

    private TextView mText;
    private CheckBox mCheckBox;

    public ExtendedCheckBoxListView(Context context, ExtendedCheckBox aCheckBoxifiedText) {
         super(context);
         
         mText.setFocusable(false);
         mText.setFocusableInTouchMode(false);

         mCheckBox.setFocusable(false);
         mCheckBox.setFocusableInTouchMode(false);
               
    }
}
Rabi
fonte
3
Gosto da sua solução: se houver apenas um widget "clicável" por linha, negar a capacidade de foco é uma solução elegante. Isso significa que um toque em qualquer lugar da linha corresponderá, mas esse é um comportamento útil quando o widget é um rádio ou uma caixa de seleção. O mesmo pode ser alcançado em XML usando android: focusable = "false" e android: focusableInTouchMode = "false".
Pierre-Luc Paour de
1
o modo xml parece não funcionar se você mudar a visibilidade, então é preciso fazer isso por meio do código após definir a "Visualização" como visível
max4ever
13

Eu tenho o mesmo problema: OnListItemClick não disparou! [RESOLVIDO]
Isso acontece na classe que estende ListActivity,
com um layout para ListActivity que contém TextBox e ListView aninhados em LinearLayout
e outro layout para as linhas (um CheckBox e TextBox aninhados em LineraLayout).

Este é o código:

res / layout / configpage.xml (principal para ListActivity)

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent" > 
    <TextView  
        android:id="@+id/selection"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="pippo" /> 
    <ListView  
        android:id="@android:id/list"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:drawSelectorOnTop="false"  
        android:background="#aaFFaa" > 
    </ListView> 
<LinearLayout> 

res/layout/row.xml (layout for single row)  
<LinearLayout  
  xmlns:android="http://schemas.android.com/apk/res/android"  
  android:layout_width="fill_parent"  
  android:layout_height="wrap_content"> 
  <CheckBox  
      android:id="@+id/img"  
      android:layout_width="wrap_content"  
      android:layout_height="wrap_content"  
      **android:focusable="false"**  
      **android:focusableInTouchMode="false"** /> 

  <TextView  
      android:id="@+id/testo"  
      android:layout_width="wrap_content"  
      android:layout_height="wrap_content"  
      **android:focusable="false"**  
      **android:focusableInTouchMode="false"** /> 
</LinearLayout> 

src /.../.../ ConfigPage.java

public class ConfigPage extends ListActivity
{
    TextView selection;

    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.configpage);  
        // loaded from res/value/strings  
        String[] azioni = getResources().getStringArray(R.array.ACTIONS);  
        setListAdapter(new ArrayAdapter&lt;String&gt;(this, R.layout.row, 
                R.id.testo,   azioni));  
        selection = (TextView) findViewById(R.id.selection);  
    }       

    public void onListItemClick(ListView parent, View view, int position, long id)
    {
        selection.setText(" " + position);
    }
}

Isso começou a funcionar quando adicionei row.xml

  • android:focusable="false"
  • android:focusableInTouchMode="false"

Eu uso Eclipse 3.5.2
Android SDK 10.0.1
min SDK versão: 3

Espero que seja útil
... e desculpe pelo meu inglês :(

rfellons
fonte
@fellons não sei por que, mas não funcionou para mim. Meu onListItemClick não está sendo chamado.
likejudo
Você adicionou o código de propriedades android: focusable = "false" e android: focusableInTouchMode = código "false"?
rfellons de
2

basta adicionar android:focusable="false"como um dos atributos do seu botão

John
fonte
1

Eu tive o mesmo problema com ToggleButton. Depois de meio dia batendo minha cabeça contra a parede, eu finalmente resolvi. É tão simples quanto deixar a visão focalizável sem foco, usando 'android: focalizável'. Você também deve evitar brincar com o foco e a capacidade de clicar (acabei de criar palavras) da linha da lista, apenas deixe-as com o valor padrão.

Obviamente, agora que suas visualizações focalizáveis ​​na linha da lista não podem ser focalizadas, os usuários que usam o teclado podem ter problemas, bem, focalizá-los. Não é provável que seja um problema, mas apenas no caso de você querer escrever aplicativos 100% perfeitos, você pode usar o evento onItemSelected para tornar os elementos da linha selecionada focalizáveis ​​e os elementos da linha previamente selecionada não focalizáveis.

Ridículo
fonte
Eu usei essa abordagem. É a maneira mais simples, se você puder contornar o problema de não ser capaz de se concentrar em itens individuais. Isso também mantém os drawables de estado para os itens da lista como deveriam ser, o que algumas das outras soluções alternativas não fazem.
Jonathan S.
1
 ListView lv = getListView();
lv.setTextFilterEnabled(true);

lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
    int position, long id) {
  // When clicked, show a toast with the TextView text
  Toast.makeText(getApplicationContext(), ((TextView) view).getText(),
      Toast.LENGTH_SHORT).show();
}
});
confúcio
fonte
-1

Eu usei getListAdapter (). GetItem (position) instanciando um Object que contém meus valores dentro do item

MyPojo myPojo = getListAdapter().getItem(position);

em seguida, usado o método getter do myPojo, ele chamará seus valores apropriados dentro do item.

Mikey
fonte