Jak odpalić onListItemClick w Listactivity z przyciskami na liście?

89

Mam prostą ListActivity, która używa niestandardowego ListAdapter do generowania widoków na liście. Zwykle ListAdapter po prostu wypełniłby widoki TextViews, ale teraz chcę również umieścić tam przycisk.

Z mojego zrozumienia i doświadczenia wynika jednak, że umieszczenie widoku z możliwością fokusu w elemencie listy zapobiega uruchomieniu onListItemClick () w ListActivity po kliknięciu elementu listy. Przycisk nadal działa normalnie w elemencie listy, ale po naciśnięciu czegoś innego niż przycisk chcę, aby wyzwalany był onListItemClick.

Jak mogę to zrobić?

CodeFusionMobile
źródło
5
Twoje rozwiązanie z potomkiem Focusability jest naprawdę pomocne, powinieneś dodać je jako odpowiedź i zaakceptować!
Maksym Gontar
@Max Powodem, dla którego tego nie robię, jest to, że jest to naprawdę zła praktyka, obejście. Gdybym kiedykolwiek znalazł trwałe, zdrowe rozwiązanie, dałbym taką odpowiedź (o ile pamiętam, że napisałem to pytanie rok temu :))
CodeFusionMobile
Chciałbym również zobaczyć dostępne obejście. Próbowałem ustawić fokus podrzędny i nie mogę go zmusić do pracy z przyciskami. Próbowałem również umieścić GridView (z ImageViews w) w wierszu listy i ma podobne problemy.
MrPurpleStreak
Odpowiedź IMHO, którą podałem, jest znacznie bardziej eleganckim rozwiązaniem problemu niż to zaproponowane przez Ramps i Praveen. Ps: Nie próbuję ożywić zapomnianego pytania, ale widzę, że nie zaakceptowałeś jeszcze żadnej odpowiedzi; D
Ewoks
@CodeFusionMobile Czy mógłbyś przyjąć odpowiedź Ewoksa? Odpowiedź z najwyższą liczbą głosów jest wadliwa, ponieważ wyłącza animację onclick dla elementu ListView (gdzie zmienia kolor na niebieski). Oszczędziłoby to innym programistom trochę czasu spędzonego na wypróbowaniu najlepszej odpowiedzi, odkryciu, że jest wadliwa, a następnie udaniu się do Ewoksa.
1 ''

Odpowiedzi:

99

tak jak pisałem w poprzednim komentarzu rozwiązaniem jest setFocusable (false) na ImageButton.

Jest jeszcze bardziej eleganckie rozwiązanie, które spróbuj dodać android:descendantFocusability="blocksDescendants" w głównym układzie elementu listy. To sprawi, że kliknięcia onListItem będą możliwe i osobno możesz obsłużyć kliknięcia Button lub ImageButton

Mam nadzieję, że to pomoże ;)

Twoje zdrowie

Ewoki
źródło
3
android:descendantFocusability="blocksDescendants"było perfekcyjne.
Matthew Mitchell,
1
ty, przyjacielu, jesteś moim osobistym bohaterem. powinieneś być najlepszym głosem! jest to o wiele lepsze w prawie każdym przypadku!
OschtärEi
To powinna być akceptowana odpowiedź. U mnie zadziałało idealnie. Wygląda na to, że wszystkie widoki w elemencie listy muszą być nieostre, a ten robi to właśnie bez ustawiania każdego z osobna.
pęd
1
Wydaje mi się, że to nie działa. Czy przez układ główny masz na myśli układ wierszy lub miejsce, w którym zdefiniowano widok listy? W każdym razie wypróbowałem wszystkie możliwe kombinacje ... nie działają :(
GreenBee
"Główny układ elementu listy", jak napisałem .. Powiedzmy, że masz listę składającą się z układów (nazwij je głównym układem elementu listy), które mają kilka Button, ImageButton, TextVeiw ..
Ewoks
59

Mam nadzieję, że mogę tu pomóc. Zakładam, że masz niestandardowy układ elementów listView, a ten układ składa się z przycisku i kilku innych widoków - takich jak TextView, ImageView lub cokolwiek innego. Teraz chcesz, aby inne zdarzenie było uruchamiane po kliknięciu przycisku, a inne zdarzenie było uruchamiane po kliknięciu wszystkich pozostałych.

Możesz to osiągnąć bez użycia onListItemClick () swojego ListActivity.
Oto, co musisz zrobić:

Używasz niestandardowego układu, więc prawdopodobnie nadpisujesz metodę getView () z niestandardowego adaptera. Sztuczka polega na tym, aby ustawić różnych słuchaczy dla swojego przycisku i różnych dla całego widoku (wiersza). Spójrz na przykład:

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

Twoja klasa OnItemClickListener mogłaby zostać zadeklarowana w następujący sposób:

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

Oczywiście prawdopodobnie dodasz więcej parametrów do konstruktora OnItemClickListener.
I jedna ważna rzecz - implementacja getView pokazana powyżej jest dość brzydka, normalnie powinieneś używać wzorca ViewHolder, aby uniknąć wywołań findViewById .. ale prawdopodobnie już to wiesz.
Mój plik custom_row.xml to RelativeLayout z przyciskiem o identyfikatorze „button”, TextView o identyfikatorze „text” i ImageView o identyfikatorze „image” - żeby wszystko było jasne.
Pozdrowienia!

Rampy
źródło
9
Moje rozwiązanie poszło w inną stronę. Rozwiązałem problem, ustawiając właściwość potomną Focusability układu wiersza listy na blokowanie potomków i nagle zadziałało. Teraz używam simpleCursorAdapter z dołączonym do niego niestandardowym wizjerem, aby skonfigurować zdarzenia i inne fantazyjne rzeczy związane z przyciskami.
CodeFusionMobile,
3
Skorzystałem z tego rozwiązania. Jednak ostrzeżenie: jeśli procedura obsługi onClick powoduje zmianę zawartości listy, co spowodowałoby wywołanie getView ORAZ jeśli widżet jest stanowy (jak ToggleButton lub CheckBox), ORAZ getView ustawia ten stan, możesz chcieć ustawić ten stan jako SetOnClickListener (null) przed zmianą stanu w onView, aby uniknąć uzyskania efektu pętli.
Pierre-Luc Paour
3
co z moim rozwiązaniem? Mam wrażenie, że jest bardziej elegancki i można go łatwiej nakładać, kiedy tylko tego potrzebujemy ..
Ewoks
Pierwszy komentarz powinien zostać zaakceptowany jako odpowiedź, więc ludzie tacy jak ja nie potrzebuje godziny, żeby go znaleźć
keybee
Dzięki! To było przydatne. Podoba mi się to jako bardziej zlokalizowana alternatywa dla podejścia focus: blockDescendants opisanego w innych wątkach.
Yoav Shapira
18

Kiedy niestandardowy ListView zawiera elementy, które można ustawić, onListItemClick nie będzie działać (myślę, że jest to oczekiwane zachowanie). Po prostu usuń fokus z widoku niestandardowego, to załatwi sprawę:

Na przykład:

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
źródło
3
Podoba mi się twoje rozwiązanie: jeśli w każdym wierszu jest tylko jeden „klikalny” widżet, to odmowa ustawiania ostrości jest eleganckim rozwiązaniem. Oznacza to, że dotknięcie dowolnego miejsca w wierszu będzie pasować, ale jest to przydatne zachowanie, gdy widżet jest radiem lub polem wyboru. To samo można osiągnąć w XML za pomocą android: focusable = "false" i android: focusableInTouchMode = "false".
Pierre-Luc Paour,
1
sposób xml wydaje się nie działać, jeśli zmienisz widoczność, więc trzeba to zrobić za pomocą kodu po ustawieniu "Widok" na widoczny
max4ever
13

Mam ten sam problem: OnListItemClick nie odpalił! [ROZWIĄZANE] Dzieje
się tak w klasie, która rozszerza ListActivity,
z układem dla ListActivity, w którym zawartość TextBox i ListView są zagnieżdżone w LinearLayout
i inny układ dla wierszy (CheckBox i TextBox zagnieżdżone w LineraLayout).

To jest kod:

res / layout / configpage.xml (główny dla 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);
    }
}

To zaczęło działać, kiedy dodałem row.xml

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

Używam Eclipse 3.5.2
Android SDK 10.0.1
min Wersja SDK: 3

Mam nadzieję, że to jest pomocne
... i przepraszam za mój angielski :(

rfellons
źródło
@fellons nie wiem dlaczego, ale to nie działa dla mnie. Mój onListItemClick nie jest wywoływany.
likejudo
Czy dodano kod właściwości android: focusable = "false" i android: focusableInTouchMode = "false"?
rfellons
2

po prostu dodaj android:focusable="false"jako jeden z atrybutów swojego przycisku

Jan
źródło
1

Miałem ten sam problem z ToggleButton. Po pół dnia walenia głową w ścianę w końcu rozwiązałem to. Jest to tak proste, jak wyłączenie ostrości widoku z możliwością ogniskowania za pomocą „android: focusable”. Powinieneś także unikać grania z możliwością ustawiania ostrości i klikalnością (właśnie wymyśliłem słowa) wiersza listy, po prostu pozostaw je z wartością domyślną.

Oczywiście teraz, gdy na widokach, na które można ustawić fokus w wierszu listy, nie można ustawić fokusu, użytkownicy korzystający z klawiatury mogą mieć problemy z ich ustawieniem. Prawdopodobnie nie będzie to problemem, ale na wypadek, gdybyś chciał pisać aplikacje w 100% bezbłędne, możesz użyć zdarzenia onItemSelected, aby ustawić fokus na elementach wybranego wiersza i na elementach wcześniej wybranego wiersza.

Śmieszny
źródło
Użyłem tego podejścia. To najprostszy sposób, jeśli możesz obejść problem braku możliwości skupienia się na poszczególnych przedmiotach. Dzięki temu stan elementów rysunkowych elementów listy pozostaje taki, jaki powinien być, czego nie zapewniają niektóre inne obejścia.
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();
}
});
konfucjusz
źródło
-1

Użyłem metody getListAdapter (). GetItem (pozycja) do utworzenia wystąpienia obiektu, który przechowuje moje wartości w elemencie

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

następnie użyje metody pobierającej z myPojo, wywoła odpowiednie wartości w elemencie.

Mikey
źródło