64 Миллисекунды После Клика

Если ваше приложение загружает данные из Интернета, отображает их в ListView и обрабатывает щелчки по ячейкам, вы можете продолжить чтение.

Это история о том, как можно рисовать за 64 мс после нажатия на ячейку списка.

У нас был обычный список, в котором было 2 типа ячеек: некликабельные категории и кликабельные ячейки.



64 миллисекунды после клика

Случайное изображение с подкатегориями Адаптер, который мы использовали, можно увидеть здесь: github.com/siyusong/foodtruck-master-android/blob/master/src/com/foodtruckmaster/android/adapter/SeparatedListAdapter.java Данные загружались с сервера, отображались в ListView, а при нажатии на ячейку открывался отдельный экран с подробным описанием.

Для обработки кликов мы использовали AdapterView.OnItemClickListener. Наши адаптеры в getItem возвращали объекты, которые были переданы на подробные экраны.

Обработка кликов производилась так:

  
  
  
   

public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Description desc = parent.getItemAtPosition(position); DescriptionActivity.open(context, desc); }

Сбои ClassCastException(String -> Описание) начали появляться в Crashlytics. Это означало, что некликабельные подзаголовки в списках все равно кликались и вместо объекта «Описание» мы получали строку.

Щелкать по некликабельным ячейкам можно с помощью PerformItemClick, но мы такие методы не использовали и были сбои на всех экранах со списками и подзаголовками, хотя их было всего несколько.

Далее углубимся в исходники 4.2.2 AbsListView, метод onTouchEvent

case MotionEvent.ACTION_UP: { switch (mTouchMode) { case TOUCH_MODE_DOWN: case TOUCH_MODE_TAP: case TOUCH_MODE_DONE_WAITING: .

final AbsListView.PerformClick performClick = mPerformClick; .

if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) { .

if (mTouchModeReset != null) { removeCallbacks(mTouchModeReset); } mTouchModeReset = new Runnable() { @Override public void run() { mTouchMode = TOUCH_MODE_REST; child.setPressed(false); setPressed(false); if (!mDataChanged) { performClick.run(); } } }; if (!mDataChanged && mAdapter.isEnabled(motionPosition)) { .

postDelayed(mTouchModeReset, ViewConfiguration.getPressedStateDuration()); } .

return true; } .

}

В исходники Android без пива лучше не лезть, видимо разработчики ОС руководствовались тем же принципом.

Здесь мы видим, что если мы щелкнули по ячейке списка и она включена, то через определенный интервал мы вызываем PefrormClick. В андроиде 4.2.2 этот интервал составляет 64 мс.

Вот как выглядит Runnable PerformClick

private class PerformClick extends WindowRunnnable implements Runnable { int mClickMotionPosition; public void run() { // The data has changed since we posted this action in the event queue, // bail out before bad things happen if (mDataChanged) return; final ListAdapter adapter = mAdapter; final int motionPosition = mClickMotionPosition; if (adapter != null && mItemCount > 0 && motionPosition != INVALID_POSITION && motionPosition < adapter.getCount() && sameWindow()) { final View view = getChildAt(motionPosition - mFirstPosition); // If there is no view, something bad happened (the view scrolled off the // screen, etc.) and we should cancel the click if (view != null) { performItemClick(view, motionPosition, adapter.getItemId(motionPosition)); } } } }

Этот исполняемый объект вызывает PerformItemClick, где уже вызван наш OnItemClickListener. Видим, что если данные в адаптере изменились, то скачиваем их.

Проверяем границы адаптера и так далее.

Самое интересное, что если вы установите новый адаптер, а не меняете данные в старом, то mDataChanged будет равно false, также стоит отметить отсутствие проверки ячеек isEnabled. Те.

нажимаем на ячейку, меняем адаптер в течение 64 мс, этот исполняемый файл выполняется и в результате клик происходит не по тем данным, которые мы видели на телефоне, а по новым.

Причём, если в новом адаптере у ячейки isEnabled=false, то по ней всё равно будет клик, вызовется onItemClickListener. Итак, в строке:

Description desc = parent.getItemAtPosition(position);

мы чудесным образом получили исключение ClassCastException Решение: очевидно, это ошибка ОС, и самым простым решением будет установка флага mDataChanged в значение true или очистка очереди сообщений при смене адаптера.

Заключение: Если вы кликнули по ячейке, и в этот момент новые данные были скачаны с сервера и установлены в список, то вы кликнули по новым данным (если вы создали адаптер заново).

Всегда проверяйте результат метода getItemAtPosition на наличие нуля и экземпляра, если у вас есть несколько типов ячеек и объектов элементов.

Теги: #Android #Разработка Android

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.