Comment lancer onListItemClick dans Listactivity avec des boutons dans la liste?

89

J'ai un ListActivity simple qui utilise un ListAdapter personnalisé pour générer les vues dans la liste. Normalement, le ListAdapter remplirait simplement les vues avec TextViews, mais maintenant je veux y mettre également un bouton.

Cependant, selon ma compréhension et mon expérience, mettre une vue focalisable dans l'élément de liste empêche le déclenchement de onListItemClick () dans ListActivity lorsque l'utilisateur clique sur l'élément de liste. Le bouton fonctionne toujours normalement dans l'élément de liste, mais quand quelque chose en plus du bouton est enfoncé, je veux que onListItemClick soit déclenché.

Comment puis-je faire fonctionner cela?

CodeFusionMobile
la source
5
votre solution avec le descendant Focusability est vraiment utile, vous devez l'ajouter comme réponse et l'accepter!
Maksym Gontar
@Max La raison pour laquelle je ne le fais pas est que c'est vraiment une mauvaise pratique, une solution de contournement. Si jamais je trouvais une solution saine et permanente, j'en ferais une réponse (si je me souviens que j'ai écrit cette question il y a un an :))
CodeFusionMobile
J'aimerais également voir la solution de contournement que vous avez. J'ai essayé de définir le focus descendant et je n'arrive pas à le faire fonctionner avec les boutons. J'ai également essayé de mettre un GridView (avec ImageViews) dans la ligne de liste et qui présente des problèmes similaires.
MrPurpleStreak
La réponse IMHO que j'ai donnée est une solution beaucoup plus élégante pour le problème que celle proposée par Ramps et Praveen. PsNe pas essayer de faire revivre une question oubliée ici mais je vois que vous n'avez pas encore accepté de réponse; D
Ewoks
@CodeFusionMobile Pouvez-vous accepter la réponse d'Ewoks? La réponse la plus votée est imparfaite car elle désactive l'animation onclick pour l'élément ListView (où il devient bleu). Cela permettrait aux autres développeurs de gagner du temps à essayer la meilleure réponse, à découvrir qu'elle est imparfaite, puis à aller chez Ewoks.
1 ''

Réponses:

99

comme je l'ai écrit dans le commentaire précédent, la solution consiste à définirFocusable (false) sur ImageButton.

Il existe une solution encore plus élégante pour essayer d'ajouter android:descendantFocusability="blocksDescendants" la mise en page racine de l'élément de liste. Cela rendra possible les clics surListItem et séparément vous pourrez gérer les clics sur Button ou ImageButton

J'espère que ça aide ;)

À votre santé

Ewoks
la source
3
android:descendantFocusability="blocksDescendants"était parfait.
Matthew Mitchell
1
toi, mon ami, tu es mon héros personnel. vous devriez être le mieux voté! c'est bien mieux dans presque tous les cas!
OschtärEi
Cela devrait être la réponse acceptée. Cela a parfaitement fonctionné pour moi. Il semble que toutes les vues de l'élément de liste doivent être non focalisables et que celle-ci le fait sans définir chacune individuellement.
rushinge
1
Cela ne semble pas fonctionner pour moi. Par disposition racine, voulez-vous dire la disposition des lignes ou où la liste est définie? Quoi qu'il en soit, j'ai essayé toutes les combinaisons possibles .. ne fonctionne pas :(
GreenBee
"Mise en page racine de l'élément de liste" comme je l'ai écrit .. Disons que vous avez une liste et constituée de mises en page (appelez-les la mise en page racine de l'élément de liste) qui ont plusieurs Button, ImageButton, TextVeiw ..
Ewoks
59

J'espère que je peux aider ici. Je suppose que vous avez une disposition personnalisée pour les éléments listView, et cette disposition se compose d'un bouton et de quelques autres vues - comme TextView, ImageView ou autre. Maintenant, vous voulez avoir un événement différent déclenché sur un clic sur un bouton et un événement différent déclenché sur tout le reste cliqué.

Vous pouvez y parvenir sans utiliser onListItemClick () de votre ListActivity.
Voici ce que vous devez faire:

Vous utilisez une disposition personnalisée, donc vous remplacez probablement la méthode getView () de votre adaptateur personnalisé. L'astuce consiste à définir les différents écouteurs pour votre bouton et différents pour l'ensemble de la vue (votre ligne). Jetez un œil à l'exemple:

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");
    }
}

Votre classe OnItemClickListener pourrait être déclarée comme ici:

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);          
    }       
}

Bien sûr, vous ajouterez probablement d'autres paramètres au constructeur OnItemClickListener.
Et une chose importante - l'implémentation de getView présentée ci-dessus est assez moche, normalement vous devriez utiliser le modèle ViewHolder pour éviter les appels findViewById ... mais vous le savez probablement déjà.
Mon fichier custom_row.xml est RelativeLayout avec Button de l'id "button", TextView de l'id "text" et ImageView de l'id "image" - juste pour clarifier les choses.
Cordialement!

Rampes
la source
9
Ma solution a fini par aller d'une autre manière. J'ai résolu le problème en définissant la propriété Focusability descendant de la disposition de ligne de liste pour bloquer les descendants et tout à coup cela a fonctionné. Maintenant, j'utilise un simpleCursorAdapter avec un classeur personnalisé qui lui est attaché pour faire la configuration de l'événement et d'autres choses sophistiquées associées aux boutons.
CodeFusionMobile
3
J'ai utilisé cette solution. Cependant, un avertissement: si votre gestionnaire onClick provoque la modification du contenu de la liste, ce qui entraînerait l'appel de getView ET si votre widget est avec état (comme un ToggleButton ou CheckBox), ET getView définit cet état, vous pouvez définirOnClickListener (null) avant de modifier l'état dans onView, pour éviter d'obtenir un effet de boucle.
Pierre-Luc Paour
3
qu'en est-il de ma solution? J'ai l'impression qui est plus élégante et peut être appliquée plus facilement quand nous en avons besoin.
Ewoks
le premier commentaire devrait être accepté comme réponse, donc des gens comme moi n'auraient pas besoin d'heures pour le trouver
keybee
Merci! Cela a été utile. Je l'aime comme une alternative plus localisée à l'approche focus: blockDescendants décrite dans d'autres threads.
Yoav Shapira
18

Quand un ListView personnalisé contient des éléments focalisables, onListItemClick ne fonctionnera pas (je pense que c'est le comportement attendu). Supprimez simplement le focus de la vue personnalisée, cela fera l'affaire:

Par exemple:

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
la source
3
J'aime votre solution: s'il n'y a qu'un seul widget "cliquable" par ligne, leur refuser la focalisation est une solution élégante. Cela signifie qu'un tap n'importe où sur la ligne correspondra, mais c'est un comportement utile lorsque le widget est une radio ou une case à cocher. La même chose peut être obtenue en XML en utilisant android: focusable = "false" et android: focusableInTouchMode = "false".
Pierre-Luc Paour
1
la méthode xml ne semble pas fonctionner si vous modifiez la visibilité, il faut donc le faire via le code après avoir défini la "Vue" sur visible
max4ever
13

J'ai le même problème: OnListItemClick n'est pas déclenché! [RÉSOLU]
Cela se produit sur la classe qui étend ListActivity,
avec une disposition pour ListActivity qui contient TextBox et ListView imbriqués dans LinearLayout
et une autre disposition pour les lignes (un CheckBox et TextBox imbriqués dans LineraLayout).

C'est le code:

res / layout / configpage.xml (principal pour 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);
    }
}

Cela commence à fonctionner lorsque j'ai ajouté sur row.xml

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

J'utilise Eclipse 3.5.2
Android SDK 10.0.1
min SDK version: 3

J'espère que cela vous sera utile
... et désolé pour mon anglais :(

rfellons
la source
@fellons ne sait pas pourquoi, mais cela n'a pas fonctionné pour moi. Mon onListItemClick n'est pas appelé.
likejudo
Avez-vous ajouté le code de propriétés android: focusable = "false" et android: focusableInTouchMode = "false" code?
rfellons
2

ajoutez simplement android:focusable="false"comme l'un des attributs de votre bouton

John
la source
1

J'ai eu le même problème avec ToggleButton. Après une demi-journée à me cogner la tête contre un mur, je l'ai finalement résolu. C'est aussi simple que de rendre la vue focalisable non focalisable, en utilisant «android: focusable». Vous devez également éviter de jouer avec la focalisation et la cliquabilité (je viens de créer des mots) de la ligne de liste, laissez-les simplement avec la valeur par défaut.

Bien sûr, maintenant que vos vues focalisables dans la ligne de liste ne sont pas focalisables, les utilisateurs utilisant le clavier peuvent avoir des problèmes, eh bien, les focaliser. Ce n'est probablement pas un problème, mais juste au cas où vous voudriez écrire des applications 100% impeccables, vous pouvez utiliser l'événement onItemSelected pour rendre les éléments de la ligne sélectionnée focalisables et les éléments de la ligne précédemment sélectionnée non focalisables.

Ridicule
la source
J'ai utilisé cette approche. C'est le moyen le plus simple, si vous pouvez contourner le problème de ne pas pouvoir vous concentrer sur des éléments individuels. Cela conserve également l'état des éléments de la liste comme ils devraient l'être, ce que certaines des autres solutions de contournement ne font pas.
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();
}
});
confucius
la source
-1

J'ai utilisé getListAdapter (). GetItem (position) instanciant un objet qui contient mes valeurs dans l'élément

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

puis utilisé la méthode getter de myPojo, il appellera ses valeurs appropriées dans l'élément.

Mikey
la source