Подтвердить что ты не робот

Android - отложенные клики в ListView

У меня есть следующая структура в моем приложении:

FragmentActivity с ViewPager, содержащим несколько фрагментов, управляемых FragmentStatePagerAdapter с использованием пакета совместимости с Android 2.1

Каждый фрагмент содержит ListView. Каждый элемент в ListView имеет LinearLayout с двумя TextViews и a Button. LinearLayout и кнопка имеют onClickListeners (отдельно). При нажатии на LinearLayout начинается еще один Activity. Я заметил, что поведение кликов очень противоречиво: иногда действие выполняется немедленно, но очень часто оно задерживается, а иногда оно просто игнорируется независимо от того, сколько раз я нажимаю. Он становится еще более странным, потому что я могу нажать, и действие будет выполнено только при запуске прокрутки списка. Я пробовал различные комбинации setFocusable(false) и setSelectable(true), но, похоже, это не имеет никакого значения. Есть идеи? Я буду рад предоставить более подробную информацию.

4b9b3361

Ответ 1

В случае, если кто-нибудь задается вопросом, как я это решил. В основном мне приходилось упрощать мои макеты. Кажется, что когда у вас сложная вложенная структура, события могут занять слишком много времени, и если вы начнете прокручивать список, то событие может вызвать неправильное действие. Я урезал макеты, переключившись на RelativeLayout как можно больше, и это, казалось, помогло много.

Ответ 2

У меня была аналогичная проблема, и мне потребовалось 2 дня для ее отладки и решения. У меня есть ListAdapter, который создает несколько TextViews в LinearLayout для каждого элемента списка. Каждый TextView имеет собственный OnClickListener, потому что мне нужно обрабатывать клики по каждому элементу.

Когда я изменил реализацию, чтобы повторно использовать представления, OnClickListener перестает работать правильно. На 4.4.2 большинство кликов работало, но иногда не было никакой реакции, пока я не прокрутил список. В 2.3 первые клики не будут работать, а затем все клики, которые обрабатываются в пакете.

В моем специальном случае я создал все представление в Java-коде, а не путем раздувания ресурсов. И решающим моментом было то, что я установил LayoutParams LinearLayout, даже когда представление было повторно использовано (это кажется более безопасным, а затем предполагая, что повторно используемое представление имеет правильные параметры макета). Когда я не устанавливаю LayoutParams при повторном использовании, все работает отлично! Вот критический код:

public View getView(int position, View convertView, ViewGroup parent) {
    LinearLayout tapeLine = null;
    if (convertView != null && convertView instanceof LinearLayout && ((LinearLayout)convertView).getChildCount() == 4) tapeLine = (LinearLayout) convertView; // Reuse view
    else tapeLine = new LinearLayout(activity);
    if (convertView == null) { // Don't set LayoutParams when reusing view
        ViewGroup.LayoutParams tapeLineLayoutParams = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        tapeLine.setLayoutParams(tapeLineLayoutParams);
    }
    ScrollingTape scrollingTape = calculatorHolder.getCalculator().getScrollingTape();
    int tapeWidthPx = parent.getWidth();
    TapeLineTextSizeInfo tapeLineTextSizeInfo = calculatorHolder.getTapeLineTextSizeHelper().createTapeLineTextSizeInfo(tapeWidthPx);
    ScrollingTapeLine line = scrollingTape.getLine(position);
    tapeLine.setOrientation(LinearLayout.HORIZONTAL);
    int tapeBackgroundColor = getBackgroundColor(line);
    tapeLine.setBackgroundColor(tapeBackgroundColor);
    addColumnViews(tapeLine, line, tapeLineTextSizeInfo);
    tapeLine.setTag(R.id.scrollingtapeadapter_viewtag_position, position);
    tapeLine.setOnLongClickListener(longClickListener);
    tapeLine.setOnClickListener(remainClickListener);
    return tapeLine;
}

Какова предыстория этого странного поведения в представлении списка? Я немного отлаживал и исследовал источники в Android. Когда андроид обновляет представление, есть два важных шага onMeasure и onLayout. Метод getView в ListAdapter призван не только рисовать представление, но и раньше во время onMeasure. В этом более позднем случае представление создается, но оно еще не зарегистрировано в цепочке событий для обработки событий щелчка.

Когда представление, которое было создано для onMeasure, повторно используется позже, чтобы быть нарисованным на экране, оно должно быть зарегистрировано в системе Android для обработки событий щелчка. Для этого особого случая разработчики Android сделали что-то, что можно было бы назвать грязным взломом. Специальный флаг в LayoutParams используется для определения того, что представление должно быть зарегистрировано при изменении события.

Теперь моя проблема: сбросив LayoutParams также при повторном использовании представления, этот флаг всегда был reset. Поэтому система Android не будет регистрировать представление, и события не пройдут.

чтобы подвести итог: при повторном представлении представления в getView в ListAdapter не перезаписывайте LayoutParams, потому что они сохраняют внутреннюю информацию системы Android.

Ответ 3

Я столкнулся с одной и той же проблемой, но в моем случае решение заключалось не в том, чтобы сохранить ссылки на представления, что вызвало проблемы с кешированием ListView. После правильного внедрения метода getView() с использованием convertView все странное поведение с потерянными/неожиданными щелчками мыши исчезло.

Ответ 4

Что сработало для меня, было назначение OnItemClickListener ListView через setOnItemClickListener, а не OnClickListener для отдельных элементов списка. Очевидно, что кнопка все еще нуждается в ее OnClickListener, но я не тестировал этот сценарий.

Ответ 5

Не уверен, что это кому-то помогает, но вместо этого у меня была аналогичная проблема с TableLayout. Вышеупомянутые решения не устранили мою проблему.

Для меня проблема была: android:animateLayoutChanges="true"

Удаление этого разрешено, чтобы моя кнопка нажала на мои строки TableLayout для правильной работы. Затем мне пришлось анимировать мои взгляды вручную вместо того, чтобы полагаться на вышеприведенное свойство.

Ответ 6

Кажется, что вы выполняете какой-то процесс блокировки (например, вызываете веб-сервисы или открываете файлы) в потоке событий, поэтому поток событий заблокирован. Если это так, пожалуйста, обработайте свой блокирующий код в другой поток, кроме события THread.