Ленивая загрузка изображений в ListView на Android

Я реализовал отложенную загрузку изображений в моем ListView. Я использую AsyncTask, чтобы загрузить изображение из Интернета и привязать его к ImageView в UIThread.

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

Я предполагаю, что проблема связана с повторным использованием convertView в BaseAdapter. Есть идеи, как это решить?

Большое спасибо.

РЕДАКТИРОВАТЬ: я отправляю ответ следующим образом:

public void setBitmap(int position, Bitmap image) {
    View itemView = mListView.getChildAt(position - mListView.getFirstVisiblePosition());
    if (itemView != null) {
        ImageView itemImageView = (ImageView) itemView.findViewById(R.id.item_imageview);
        itemImageView.setImageBitmap(image);
    }
}

person shiami    schedule 22.09.2010    source источник
comment
Я пробовал здесь пример android-developers.blogspot.com/2010 / 07 / Работает, но есть шанс привязать не то изображение. Уловка сравнения экземпляра задачи кажется такой же, как ответ Януша. Но он использует слабую ссылку для хранения экземпляра ImageView. Нужно копать глубже. Любые идеи?   -  person shiami    schedule 24.09.2010


Ответы (2)


Создайте в своем адаптере функцию с именем void setBitmap(Bitmap bitmap, int position) или аналогичную. Позвольте вашей AsyncTask вызывать этот метод, когда доступно новое растровое изображение. Затем этот метод может вызвать notifyDataSetChanged() в самом потоке пользовательского интерфейса, чтобы обеспечить обновление представлений. Хранение ссылок на представления в адаптере (даже при удерживании их в AsyncTask) опасно!

person mreichelt    schedule 22.09.2010
comment
Это не проблема шиами. Он повторно использует представления, для которых задано изображение, и поскольку правильное изображение установлено в асинхронной задаче, старое изображение отображается до тех пор, пока не будет загружено новое изображение и т. Д. - person Janusz; 22.09.2010
comment
Да, это. Однажды я работал над проектом, в котором происходило именно такое поведение, и использование setTag () или getTag () было просто уродливым решением. Решением было установить растровое изображение непосредственно в методе getView (), который сам запускается функцией notifyDataSetChanged (). Конечно, это приведет к обновлению всех видимых представлений, что может вызвать заметное заикание. Чтобы обновить только одно представление, может быть лучше это решение: stackoverflow.com/questions/3724874/ - person mreichelt; 22.09.2010
comment
Спасибо за совет. Но где я могу получить правильный экземпляр представления для привязки загруженного изображения в соответствии с заданной позицией? - person shiami; 23.09.2010
comment
Я попробовал решение stackoverflow.com/questions/ 3724874 /, но это приводит к тому, что NullPointerException при прокрутке быстро меняется. Кажется, getFirstVisiblePosition () изменен и не может получить правильную позицию. - person shiami; 23.09.2010
comment
Конечно, решение на stackoverflow.com/ questions / 3724874 / может вернуть значение null, если представление не отображается в данный момент, поэтому вы должны это проверить. Я уже добавил комментарий по этому поводу. - person mreichelt; 23.09.2010
comment
Здорово! Извините, я не видел вашего комментария. Теперь все работает гладко. Большое спасибо! - person shiami; 24.09.2010

При отложенной загрузке изображений в ListView возникают две проблемы.

  1. Старые изображения будут отображаться до тех пор, пока не будут загружены новые. Это легко, просто установите ImageView на изображение при загрузке представления или сделайте его невидимым перед началом загрузки изображения.
  2. Вторую проблему решить сложнее. Представьте, что вы очень быстро прокручиваете свой список. Теперь ваши представления могут быть переработаны до того, как старая AsyncTask завершит загрузку изображения. Теперь у вас есть две запущенные задачи, которые в методе onPostExecute установят изображение в изображение. Теперь на короткое время будет отображаться неправильное изображение, пока не завершится вторая задача, или, что еще хуже, по какой-то причине, связанной с сетью, они не завершаются в том порядке, в котором они были запущены, и у вас неправильное изображение, перезаписывающее правильное изображение. Чтобы решить эту проблему, вы должны проверить, какое изображение должно отображаться после завершения задачи. В классе View есть два метода для таких вещей, как этот:

    setTag и getTag Вы можете привязать любой объект к образу, который приходит вам в голову. В большинстве случаев я использую setTag для привязки URL-адреса изображения в виде строки к просмотру изображения перед запуском задачи. Теперь я могу преобразовать getTag в String после завершения задачи и сравнить URL-адрес, который должен отображаться, с URL-адресом, который я загрузил, и установить изображение только при необходимости.

person Janusz    schedule 22.09.2010
comment
Спасибо! Первая проблема, которую вы описали, мне не кажется решением. Потому что ImageView будет привязан несколько раз при быстрой прокрутке. Поэтому установка ImageView для загрузки изображения или невидимости не работает. - person shiami; 23.09.2010
comment
Кажется, что метод getTag не связывает ImageView сразу, потому что тег изменяется после прокрутки. Он обновляется только после медленной прокрутки назад и назад. Есть ли какой-то шаг, который я делаю неправильно? - person shiami; 23.09.2010
comment
Я не понимаю, что вы имеете в виду, когда getTag не связывает ImageView сразу, поэтому я не могу сказать, делаете ли вы что-то не так. - person Janusz; 23.09.2010
comment
После быстрой прокрутки и остановки где-то изображение ImageView не привязано. Мне нужно немного отодвинуть его за пределы экрана и обратно, чтобы он обновился. После того, как я попробовал решение отсюда, android-developers.blogspot.com / 2010/07 / Я думаю, что это не лучшая идея держать сильную ссылку на ImageView, поскольку он разработан для выпуска ListView. В любом случае, спасибо за вашу помощь. - person shiami; 24.09.2010
comment
С помощью решения setTag просмотр изображений все еще может быть выпущен. Ссылка из фоновой задачи перестанет существовать вместе с asyncTask, а затем задача и изображение будут освобождены. Вы не храните ссылку навсегда и позволяете огромному количеству ImageView накапливаться и пропускать память. Это просто немного больше памяти, которая используется во время выполнения задачи. - person Janusz; 27.09.2010
comment
Вам просто нужно очистить ImageView (установить для изображения значение null) перед его загрузкой. Это решит проблему неправильного отображения изображения в неправильном ImageView. - person srgtuszy; 10.06.2011