Пользовательская проблема с щелчком ListView для элементов в Android

110

Итак, у меня есть собственный объект ListView. Элементы списка имеют два текстовых представления, расположенных друг над другом, а также горизонтальную полосу выполнения, которую я хочу скрыть, пока я действительно что-то не сделаю. Справа находится флажок, который я хочу отображать только тогда, когда пользователю нужно загрузить обновления в свои базы данных. Когда я снимаю флажок, установив для видимости значение Visibility.GONE, я могу щелкать элементы списка. Когда флажок виден, я не могу щелкнуть что-либо в списке, кроме флажков. Я немного поискал, но не нашел ничего, имеющего отношение к моей текущей ситуации. Я нашел этот вопросно я использую переопределенный ArrayAdapter, поскольку я использую ArrayLists для внутреннего хранения списка баз данных. Мне просто нужно получить представление LinearLayout и добавить onClickListener, как это сделал Том? Я не уверен.

Вот XML-макет строки списка:

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

А вот класс, расширяющий ListActivity. Очевидно, что он все еще находится в разработке, так что простите то, чего не хватает или может остаться без дела:

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
}

изменить: у меня все еще есть эта проблема. Я обманываю и добавляю обработчики onClick к текстовым представлениям, но кажется чрезвычайно глупым, что моя функция onListItemClick () вообще не вызывается, когда я не нажимаю на свой флажок.

MattC
источник

Ответы:

243

Проблема в том, что Android не позволяет выбирать элементы списка, на которых есть элементы, на которые можно сфокусироваться. Я изменил флажок в элементе списка, чтобы иметь такой атрибут:

android:focusable="false"

Теперь элементы моего списка, содержащие флажки (работает и с кнопками), являются «выбираемыми» в традиционном смысле (они загораются, вы можете щелкнуть в любом месте элемента списка, и обработчик «onListItemClick» сработает и т. Д.).

РЕДАКТИРОВАТЬ: В качестве обновления комментатор упомянул: «Просто примечание, после изменения видимости кнопки мне пришлось снова программно отключить фокус».

MattC
источник
24
Похоже, это решение не работает, когда ImageButton используется в элементе списка. :(
Джулиан А.
1
Хорошо, но что, если я хочу, чтобы мой флажок и щелчок в виде списка были взаимоисключающими? Если я выбираю элемент ListView, я не обязательно хочу проверять флажок. Возможно ли это с помощью этого решения?
Mike6679
@Mike Да, это решение по-прежнему требует, чтобы флажок был явно установлен, чтобы с ним можно было взаимодействовать. Он просто повторно включает функции в ListView, которые таинственным образом игнорируются, когда у вас есть фокусируемые элементы в макете элемента списка.
MattC
2
Отличный ответ, спасибо, хорошо работает и с ImageButtons. Замечу, что после изменения видимости кнопки мне пришлось снова программно отключить фокус.
Ljdawson 03
1
вам также может потребоваться установить android: clickable = "false"
Мина Сами
43

Если у вас есть ImageButton внутри элемента списка, вы должны установить descendantFocusabilityзначение «blocksDescendants» в элементе корневого элемента списка.

android:descendantFocusability="blocksDescendants"

И focusableInTouchModeфлаг trueв поле ImageButtonзрения.

android:focusableInTouchMode="true"
Micnoy
источник
1
Это отлично работает для элемента списка, имеющего кнопки изображения. Но это может сработать, кроме ImageButton
raksja 02
12

У меня возникла аналогичная проблема, и я обнаружил, что CheckBox довольно привередлив в ListView. Что происходит, так это то, что он накладывает свою волю на весь ListItem и как бы переопределяет onListItemClick. Вы можете реализовать для этого обработчик кликов и также установить свойство text для CheckBox вместо использования TextViews.

Я бы сказал, что посмотрите и этот объект View, он может работать лучше, чем CheckBox

Проверено текстовое представление

Эндрю Берджесс
источник
6
Я обнаружил, что если вы установите для параметра «focusable» значение false для фокусируемых элементов в списке, вы можете снова использовать функцию onListItemClick.
MattC
2
Однако флажок становится оранжевым, как и элемент списка. Кто-нибудь знает решение?
erdomester
8

используйте эту строку в корневом представлении элемента списка

android: DescendantsFocusability = "blocksDescendants"

Суреш Кумар
источник