Problem z kliknięciem niestandardowego ListView w elementach w systemie Android

110

Mam więc niestandardowy obiekt ListView. Elementy listy mają dwa widoki tekstu ułożone jeden na drugim oraz poziomy pasek postępu, który chcę pozostać ukryty, dopóki coś nie zrobię. Po prawej stronie znajduje się pole wyboru, które chcę wyświetlać tylko wtedy, gdy użytkownik musi pobrać aktualizacje do swoich baz danych. Kiedy wyłączam to pole wyboru, ustawiając widoczność na Widoczność.GONE, mogę klikać elementy listy. Gdy pole wyboru jest widoczne, nie mogę kliknąć niczego na liście poza polami wyboru. Wyszukałem trochę, ale nie znalazłem nic związanego z moją obecną sytuacją. Znalazłem to pytanieale używam przesłoniętego ArrayAdapter, ponieważ używam ArrayLists do wewnętrznego przechowywania listy baz danych. Czy muszę tylko pobrać widok LinearLayout i dodać onClickListener, tak jak zrobił to Tom? Nie jestem pewny.

Oto XML układu wiersza widoku listy:

<?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>

A oto klasa, która rozszerza ListActivity. Oczywiście wciąż jest w fazie rozwoju, więc wybacz rzeczy, których brakuje lub które mogą zostać:

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
}

edycja: nadal mam ten problem. Oszukuję i dodaję moduły obsługi onClick do textviews, ale wydaje mi się bardzo głupie, że moja funkcja onListItemClick () nie jest w ogóle wywoływana, gdy nie klikam swojego pola wyboru.

MattC
źródło

Odpowiedzi:

243

Problem polega na tym, że system Android nie pozwala wybierać elementów listy, które mają elementy, na których można ustawić fokus. Zmodyfikowałem pole wyboru w elemencie listy, aby miało taki atrybut:

android:focusable="false"

Teraz moje elementy listy, które zawierają pola wyboru (działają również dla przycisków) są „wybieralne” w tradycyjnym sensie (zapalają się, możesz kliknąć w dowolnym miejscu elementu listy, a funkcja obsługi „onListItemClick” uruchomi się, itd.).

EDYCJA: Jako aktualizacja, komentator wspomniał: „Tylko uwaga, po zmianie widoczności przycisku musiałem ponownie programowo wyłączyć fokus”.

MattC
źródło
24
Wygląda na to, że to rozwiązanie nie działa, gdy ImageButton jest używany w elemencie listy. :(
Julian A.
1
Ok, ale co jeśli chcę, aby moje pole wyboru i mój widok listy wykluczały się wzajemnie? Jeśli wybiorę element ListView, niekoniecznie chcę sprawdzać checkBox. Czy to możliwe dzięki temu rozwiązaniu?
Mike6679
@Mike Tak, to rozwiązanie nadal wymaga wyraźnego kliknięcia pola wyboru w celu interakcji. Po prostu ponownie włącza funkcjonalność w ListView, która w tajemniczy sposób jest pomijana, gdy masz elementy, na które można ustawić fokus w układzie elementu listy.
MattC
2
Świetna odpowiedź, dzięki, działa również dobrze z ImageButtons. Tylko uwaga, po zmianie widoczności przycisku musiałem ponownie programowo wyłączyć fokus.
Ljdawson,
1
konieczne może być również ustawienie android: clickable = "false"
Mina Samy
43

Jeśli masz ImageButton wewnątrz elementu listy, powinieneś ustawić descendantFocusabilitywartość na 'blocksDescendants' w głównym elemencie listy.

android:descendantFocusability="blocksDescendants"

I focusableInTouchModeflaga truew ImageButtonwidoku.

android:focusableInTouchMode="true"
micnoy
źródło
1
Działa to doskonale w przypadku listitem posiadających przyciski obrazów. Ale to może zadziałać z wyjątkiem ImageButton
raksja
12

Miałem podobny problem i stwierdziłem, że CheckBox jest raczej wybredny w ListView. Co się dzieje, narzuca to na cały ListItem i w pewnym sensie zastępuje onListItemClick. Możesz chcieć zaimplementować w tym celu procedurę obsługi kliknięcia i ustawić właściwość text również dla CheckBox, zamiast używać TextViews.

Powiedziałbym, że spójrz również na ten obiekt View, może działać lepiej niż CheckBox

Sprawdzony widok tekstu

Andrew Burgess
źródło
6
Zauważyłem, że jeśli ustawisz opcję „focusable” na false dla elementów, które można ustawić na liście, możesz ponownie użyć funkcji onListItemClick.
MattC
2
Jednak pole wyboru zmienia kolor na pomarańczowy, podobnie jak element listy. Czy ktoś zna rozwiązanie?
erdomester
8

użyj tej linii w widoku głównym elementu listy

android: descendantFocusability = "blocksDescendants"

Suresh kumar
źródło