Пользовательский адаптер курсора, вызывающий bindView несколько раз

Я страдал от этой проблемы в течение многих месяцев (но теперь я настраиваю производительность). Однако теперь мне крайне необходимо знать, почему мой адаптер считает необходимым запускать bindView до 4 раз для записи.

У меня есть собственный адаптер курсора, который заполняет gridview.

Некоторая отладка, чтобы показать, что происходит:

03-08 14:46:47.980: I/AdapterCursorGrid(20724): newView()
03-08 14:46:48.470: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:48.570: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:48.570: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:48.600: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:48.690: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:49.490: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:49.501: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:49.501: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:49.521: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:49.521: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:50.320: I/AdapterCursorGrid(20724): newView()
03-08 14:46:51.170: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:51.180: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.180: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:51.180: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:51.190: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:51.190: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.190: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:51.200: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:51.870: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:51.896: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.896: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:51.900: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:51.900: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:51.900: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.900: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:51.900: D/AdapterCursorGrid(20724): bindView() Picture creation...

«Аватар пуст...» и «Создание изображения…» — это просто отладка, которая говорит мне, что она обрабатывает и обновляет 2 конкретных ImageView.

Почему bindView запускается так много раз? Каковы причины этого и что я могу сделать, чтобы решить эту проблему?

Логически говоря, я ожидаю, что bindView запустится один раз (и один раз при каждом обновлении адаптера), я ошибаюсь, думая об этом?


person HGPB    schedule 08.03.2013    source источник
comment
Каким бы ни было создание изображения, оно не должно находиться в основном потоке приложения. bindView() должен вернуться менее чем за 1 мс на GridView, независимо от того, чувствуете ли вы, что он вызывается слишком много раз по другим причинам. Тратить ~600 мс на создание изображения — это очень плохо. Даже если бы bindView() вызывалась только один раз для каждой ячейки, она вызывалась бы MxN раз (M строк, N столбцов) при первоначальном заполнении GridView, а это означает, что ваш пользовательский интерфейс зависает на несколько секунд с самого начала.   -  person CommonsWare    schedule 08.03.2013
comment
@CommonsWare Да, я добьюсь этого с помощью задачи aync.   -  person HGPB    schedule 08.03.2013


Ответы (3)


Операционная система может вызывать bindView несколько раз, чтобы правильно измерить и отобразить список. Это не столько баг, сколько так и должно быть. Именно поэтому, наряду с плавностью прокрутки, реализации bindView должны быть максимально эффективными. На странице разработчиков Android есть несколько полезных советов и приемов, которые вы можете использовать.

person CaseyB    schedule 08.03.2013
comment
Должен ли я предположить, что это относится к gridView? - person HGPB; 08.03.2013
comment
Да. Android может вызывать это столько раз, сколько потребуется для правильного измерения и компоновки контейнера. - person CaseyB; 08.03.2013
comment
В настоящее время я реализую шаблон ViewHolder. Я обновлю обработку изображений в асинхронной задаче, а затем снова просмотрю bindView. Беспокоит то, что моя асинхронная задача будет запускаться 4 раза в текущем состоянии. Любые другие предложения относительно того, почему он вызывается 4 раза? Для GridView установлено значение fill_parent как для высоты, так и для ширины. Почесывание головы. - person HGPB; 08.03.2013
comment
Суть в том, что на самом деле не имеет значения, почему он вызывается любое количество раз. Это не то, что вы можете изменить, так как это встроено в операционную систему. Все, что вы можете сделать, это сделать его максимально гладким. Это означает использование ViewHolder, использование AsyncTask для загрузки изображений из потока пользовательского интерфейса и кеширование этих изображений, чтобы вам не нужно было подключаться к сети, чтобы снова и снова загружать одно и то же изображение. - person CaseyB; 08.03.2013
comment
Я согласен! Это то, чего я сейчас достиг. Спасибо. - person HGPB; 09.03.2013

Я также обнаружил, что bindView вызывается гораздо чаще, чем ожидалось. Моя высота ListView была установлена ​​​​на wrap_content. После изменения на match_parent количество вызовов резко сократилось.

Кредит за это решение относится к ответу на этот вопрос >BindView пользовательского CursorAdapater вызывается 77 раз... я сделал что-то не так?

person aaronmarino    schedule 10.06.2014
comment
почему за этот ответ еще не проголосовали? Это отличное решение. - person faizal; 12.09.2014
comment
Ну, технически это не решение заданного вопроса. Я просто хотел подчеркнуть, что если вы видите, что bindView вызывается чрезмерное количество раз, это может быть связано с тем, что для высоты ошибочно установлено значение wrap_content. - person aaronmarino; 22.07.2015
comment
Спасибо, вы спасли мой день!! Этот ответ должен быть принятым ответом :) - person Riccardo Leschiutta; 28.07.2017

Одна вещь, которую я обнаружил с ImageView, заключается в том, что изменение изображения заставит ImageView запрашивать макет, если только новое и старое изображение не имеют точно такого же размера.

Внутри ImageView использует getIntrinsicWidth() и getIntrinsicHeight(), чтобы решить, запрашивать ли макет.

Попробуйте что-то вроде этого (хотя было бы разумнее отправить текущую ширину/высоту в вашу асинхронную загрузку и изменить размер растрового изображения перед возвратом из фона):

public void replaceBitmap(ImageView iv, Bitmap bitmap) {
    Drawable current = iv.getDrawable();
    if (bitmap.getWidth() != current.getIntrinsicHeight()
            || bitmap.getHeight() != current.getIntrinsicHeight()) {
        bitmap = Bitmap.createScaledBitmap(bitmap,
            current.getIntrinsicWidth(), current.getIntrinsicHeight(), true);
    }
    iv.setImageBitmap(bitmap);
}
person Dave Cole    schedule 26.04.2014