Wie wird onListItemClick in Listactivity mit Schaltflächen in der Liste ausgelöst?

89

Ich habe eine einfache ListActivity, die einen benutzerdefinierten ListAdapter verwendet, um die Ansichten in der Liste zu generieren. Normalerweise füllt der ListAdapter die Ansichten nur mit TextViews, aber jetzt möchte ich dort auch eine Schaltfläche einfügen.

Nach meinem Verständnis und meiner Erfahrung verhindert das Einfügen einer fokussierbaren Ansicht in das Listenelement das Auslösen von onListItemClick () in der ListActivity, wenn auf das Listenelement geklickt wird. Die Schaltfläche funktioniert weiterhin normal innerhalb des Listenelements, aber wenn etwas anderes als die Schaltfläche gedrückt wird, soll onListItemClick ausgelöst werden.

Wie kann ich das zum Laufen bringen?

CodeFusionMobile
quelle
5
Ihre Lösung mit Nachkommen Fokussierbarkeit ist wirklich hilfreich. Sie sollten sie als Antwort hinzufügen und akzeptieren!
Maksym Gontar
@Max Der Grund, warum ich das nicht tue, ist, dass es wirklich schlechte Praxis ist, eine Problemumgehung. Wenn ich jemals eine dauerhafte gesunde Lösung finden würde, würde ich dies als Antwort geben (wenn ich mich erinnere, dass ich diese Frage vor einem Jahr geschrieben habe :))
CodeFusionMobile
Ich würde auch gerne sehen, welche Problemumgehung Sie haben. Ich habe versucht, den Nachkommenfokus festzulegen, und kann ihn nicht mit Schaltflächen zum Laufen bringen. Außerdem habe ich versucht, eine GridView (mit ImageViews) in die Listenzeile einzufügen, und das hat ähnliche Probleme.
MrPurpleStreak
IMHO Antwort, die ich gab, ist eine viel elegantere Lösung für das Problem als eine von Ramps und Praveen vorgeschlagene. PsNot versucht, vergessene Frage hier wiederzubeleben, aber ich sehe, Sie haben noch keine Antwort akzeptiert; D
Ewoks
@CodeFusionMobile Könnten Sie bitte die Antwort von Ewoks akzeptieren? Die Antwort mit der höchsten Bewertung ist fehlerhaft, da sie die Onclick-Animation für das ListView-Element deaktiviert (wo sie blau wird). Dies würde anderen Entwicklern Zeit sparen, wenn sie versuchen, die beste Antwort zu finden, herauszufinden, dass sie fehlerhaft ist, und dann zu Ewoks gehen.
1 ''

Antworten:

99

Wie ich in früheren Kommentaren geschrieben habe, besteht die Lösung darin, auf ImageButtonFocusable (false) zu setzen.

Es gibt noch elegantere Lösungen, die versuchen, das android:descendantFocusability="blocksDescendants" Stammlayout des Listenelements hinzuzufügen . Das macht Klicks auf ListItem möglich und separat können Sie Button- oder ImageButton-Klicks verarbeiten

Ich hoffe es hilft ;)

Prost

Ewoks
quelle
3
android:descendantFocusability="blocksDescendants"war perfekt.
Matthew Mitchell
1
Du, mein Freund, bist mein persönlicher Held. du solltest der bestgewählte sein! das ist in fast allen Fällen viel viel besser!
OschtärEi
Dies sollte die akzeptierte Antwort sein. Es hat perfekt für mich funktioniert. Es sieht so aus, als müssten alle Ansichten im Listenelement nicht fokussierbar sein, und dies tut genau das, ohne sie einzeln festzulegen.
Rushinge
1
Das scheint bei mir nicht zu funktionieren. Mit Stammlayout meinen Sie das Zeilenlayout oder wo die Listenansicht definiert ist? Jedenfalls habe ich alle möglichen Kombinationen ausprobiert. Funktioniert nicht :(
GreenBee
"Stammlayout des Listenelements", wie ich geschrieben habe. Nehmen wir also an, Sie haben eine Liste und bestehen aus Layouts (nennen Sie sie Stammlayout des Listenelements) mit mehreren Schaltflächen, ImageButton, TextVeiw.
Ewoks
59

Ich hoffe ich kann hier helfen. Ich gehe davon aus, dass Sie ein benutzerdefiniertes Layout für listView-Elemente haben und dieses Layout aus Schaltflächen und einigen anderen Ansichten besteht - wie TextView, ImageView oder was auch immer. Jetzt möchten Sie, dass beim Klicken auf die Schaltfläche ein anderes Ereignis und bei allen anderen Klicks ein anderes Ereignis ausgelöst wird.

Sie können dies erreichen, ohne onListItemClick () Ihrer ListActivity zu verwenden.
Folgendes müssen Sie tun:

Sie verwenden ein benutzerdefiniertes Layout, daher überschreiben Sie wahrscheinlich die getView () -Methode Ihres benutzerdefinierten Adapters. Der Trick besteht darin, die verschiedenen Listener für Ihre Schaltfläche und für die gesamte Ansicht (Ihre Zeile) unterschiedlich einzustellen. Schauen Sie sich das Beispiel an:

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

Ihre OnItemClickListener-Klasse könnte wie folgt deklariert werden:

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

Natürlich werden Sie dem OnItemClickListener-Konstruktor wahrscheinlich weitere Parameter hinzufügen.
Und eine wichtige Sache - die oben gezeigte Implementierung von getView ist ziemlich hässlich. Normalerweise sollten Sie das ViewHolder-Muster verwenden, um findViewById-Aufrufe zu vermeiden. Aber das wissen Sie wahrscheinlich bereits.
Meine custom_row.xml-Datei ist RelativeLayout mit Button der ID "button", TextView der ID "text" und ImageView der ID "image" - nur um die Dinge klarer zu machen.
Grüße!

Rampen
quelle
9
Meine Lösung ging einen anderen Weg. Ich habe das Problem gelöst, indem ich die Nachkommen-Fokussierbarkeitseigenschaft des Listenzeilenlayouts so eingestellt habe, dass Nachkommen blockiert werden, und plötzlich hat es funktioniert. Jetzt verwende ich einen simpleCursorAdapter mit einem daran angehängten benutzerdefinierten Ansichtsordner, um das Ereignis-Setup und andere ausgefallene Dinge zu erledigen, die mit den Schaltflächen verbunden sind.
CodeFusionMobile
3
Ich habe diese Lösung verwendet. Eine Warnung: Wenn Ihr onClick-Handler bewirkt, dass sich der Inhalt der Liste ändert, was dazu führen würde, dass getView aufgerufen wird UND wenn Ihr Widget statusbehaftet ist (wie ein ToggleButton oder eine CheckBox) UND getView diesen Status festlegt, möchten Sie möglicherweise OnOnClickListener setzen (null), bevor Sie den Status in onView ändern, um einen Schleifeneffekt zu vermeiden.
Pierre-Luc Paour
3
Was ist mit meiner Lösung? Ich habe den Eindruck, dass es eleganter ist und einfacher angewendet werden kann, wann immer wir es brauchen.
Ewoks
Der erste Kommentar sollte als Antwort akzeptiert werden, damit Leute wie ich keine Stunden brauchen, um ihn zu finden
keybee
Vielen Dank! Das war nützlich. Ich mag es als lokalisierte Alternative zum Fokus: blockDescendants-Ansatz, der in anderen Threads beschrieben wird.
Yoav Shapira
18

Wenn eine benutzerdefinierte ListView fokussierbare Elemente enthält, funktioniert onListItemClick nicht (ich denke, es ist das erwartete Verhalten). Entfernen Sie einfach den Fokus aus der benutzerdefinierten Ansicht, es wird den Trick tun:

Beispielsweise:

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
quelle
3
Ich mag Ihre Lösung: Wenn es nur ein "anklickbares" Widget pro Zeile gibt, ist es eine elegante Lösung, ihnen die Fokussierbarkeit zu verweigern. Dies bedeutet, dass ein Tippen an einer beliebigen Stelle in der Zeile übereinstimmt. Dies ist jedoch ein nützliches Verhalten, wenn das Widget ein Radio oder ein Kontrollkästchen ist. Dasselbe kann in XML mit android: focusable = "false" und android: focusableInTouchMode = "false" erreicht werden.
Pierre-Luc Paour
1
Die XML-Methode scheint nicht zu funktionieren, wenn Sie die Sichtbarkeit ändern. Sie müssen dies also durch Code tun, nachdem Sie die "Ansicht" auf sichtbar gesetzt haben
max4ever
13

Ich habe das gleiche Problem: OnListItemClick nicht ausgelöst! [Gelöst] Dies
geschieht in Klassen, die ListActivity erweitern,
mit einem Layout für ListActivity, in dem TextBox und ListView in LinearLayout verschachtelt sind,
und einem anderen Layout für die Zeilen (CheckBox und TextBox, die in LineraLayout verschachtelt sind).

Das ist Code:

res / layout / configpage.xml (Haupt für 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);
    }
}

Dies beginnt zu funktionieren, wenn ich row.xml hinzufüge

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

Ich benutze Eclipse 3.5.2
Android SDK 10.0.1
min SDK Version: 3

Ich hoffe das ist hilfreich
... und entschuldige mein Englisch :(

rfellons
quelle
@ Fellons weiß nicht warum, aber es hat bei mir nicht funktioniert. Mein onListItemClick wird nicht aufgerufen.
Likejudo
Haben Sie den Eigenschaftencode android: focusable = "false" und android: focusableInTouchMode = "false" hinzugefügt?
Rfellons
2

Fügen Sie einfach android:focusable="false"als eines der Attribute Ihrer Schaltfläche hinzu

John
quelle
1

Ich hatte das gleiche Problem mit ToggleButton. Nachdem ich einen halben Tag lang meinen Kopf gegen eine Wand geschlagen hatte, löste ich es endlich. Es ist so einfach wie das Fokussieren der fokussierbaren Ansicht mit 'android: focusable'. Sie sollten auch vermeiden, mit der Fokussierbarkeit und Klickbarkeit (ich habe gerade Wörter erfunden) der Listenzeile zu spielen. Lassen Sie sie einfach mit dem Standardwert.

Jetzt, da Ihre fokussierbaren Ansichten in der Listenzeile nicht mehr fokussierbar sind, können Benutzer, die die Tastatur verwenden, Probleme haben, sie zu fokussieren. Es ist wahrscheinlich kein Problem, aber nur für den Fall, dass Sie 100% fehlerfreie Apps schreiben möchten, können Sie das Ereignis onItemSelected verwenden, um die Elemente der ausgewählten Zeile fokussierbar und die Elemente der zuvor ausgewählten Zeile nicht fokussierbar zu machen.

Lächerlich
quelle
Ich habe diesen Ansatz verwendet. Dies ist der einfachste Weg, wenn Sie das Problem umgehen können, dass Sie sich nicht auf einzelne Elemente konzentrieren können. Dadurch bleiben auch die Statuszeichnungen für die Listenelemente so, wie sie sein sollten, was bei einigen anderen Problemumgehungen nicht der Fall ist.
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();
}
});
Konfuzius
quelle
-1

Ich habe getListAdapter (). GetItem (position) verwendet, um ein Objekt zu instanziieren, das meine Werte innerhalb des Elements enthält

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

Verwenden Sie dann die Getter-Methode aus dem myPojo, um die richtigen Werte innerhalb des Elements aufzurufen.

Mikey
quelle