Как определить Android View, отображаемый с асинхронной загрузкой изображения

РЕДАКТИРОВАТЬ 12.03.14: Опубликовано несколько недель, но активности нет. Достаточно ли ясен мой вопрос? Или TL;DR? К сожалению, поскольку я новый участник, я не могу предложить награду :-(

Мой вопрос можно резюмировать так:

  • Как я могу определить, что представление, содержащее асинхронно загруженные элементы (например, веб-изображения), полностью загружено и отрисовано, не прибегая к небольшому взлому — см. ниже.
  • Есть ли детерминированный способ узнать, когда представление закончило загрузку, не используя просто длинное защитное значение задержки для View.postDelayed()?

Пожалуйста, прочитайте предысторию и особенности этого вопроса, если вы думаете, что можете помочь.

Предыстория вопроса

Я использую стороннюю библиотеку в своем приложении, чтобы попытаться сделать следующее:

  1. Сторонняя библиотека асинхронно загружает некоторые данные через REST API.
  2. Library processes JSON data and uses Picasso API to download image URLs contained within the JSON response and then load them into a customised View (library view, not mine) which I then display in my Activity.
    • Note: the Picasso calls are encapsulated by the library that I am using and the 3rd party library opted not to use the callbacks provided by Picasso which indicates when the images have loaded. I merely have access to a callback when the underlying JSON request has returned successfully.
  3. После того, как пользовательский View загрузится и полностью отрендерится, я хочу сохранить этот View в хранилище как PNG.

Моя проблема возникает на шаге 3: я не могу детерминистически определить, когда пользовательский View закончил загрузку, чтобы я мог сохранить PNG после полного отображения представления. Пикассо по своему замыслу асинхронно загружает изображения в объекты Android View, и я думаю, что в результате методы, которые я пробовал ниже, не сработали:

  1. Используйте GlobalLayoutListener, чтобы определить, когда представление завершило загрузку, как предложено здесь.

    myView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {           
        @Override
        public void onGlobalLayout() {
            // Try to begin saving PNG here
            savePNG();
        });
    
    • This didn't work as onGlobalLayout() fires an unpredictable number of times before the View is completely rendered depending on how quickly the images are being loaded in the background.
    • Я использовал View.postDelayed(Runnable, delay) в onGlobalLayout(), но, опять же, невозможно узнать, как долго должна быть задержка из-за мощности устройства и пропускной способности сети, плюс это вводит своего рода состояние гонки.
  2. Во многих ответах на подобные вопросы о SO предлагалось просто опубликовать savePNG() как Runnable в очереди сообщений пользовательского интерфейса. Я думаю, что это хорошо работает для большинства сценариев, но не тогда, когда изображения асинхронно загружаются в представления через Интернет. В большинстве случаев этот подход приводит к попытке сохранить изображение до того, как представление будет полностью загружено и отображено. Конечно, я могу postDelayed, но это приводит к той же неопределенности в отношении длины задержки и условий гонки, что и в предыдущем пункте.

  3. Я попытался дождаться события MessageQueue.IdleHandler.queueIdle(), чтобы выполнить savePNG(), но, к сожалению, это также происходит до того, как все ресурсы будут загружены и отображены.

  4. Сохраните последний раз, когда был вызван onGlobalLayout, и пусть другой поток периодически проверяет «нет обновлений в XXX мс», чтобы определить, что представление успокоилось. Это не лучшее решение, и приложение может выглядеть излишне медленным, но я, по крайней мере, мог заставить его работать.

  5. Я попытался подклассифицировать View и переопределить dispatchDraw() (предложено здесь) и перезвонить моему приложению - та же проблема, что и onGlobalLayout - это срабатывает несколько раз, прежде чем представление будет полностью загружено и отображено.

Вау, длинный пост, но, надеюсь, это означает, что я не буду выглядеть так глупо, если есть очень очевидная «подводная лодка», которая все это время смотрела мне в лицо :-)


person aoemerson    schedule 13.11.2014    source источник
comment
После загрузки полного изображения мы применяем растровое изображение к изображению, я думаю, этого достаточно, чтобы узнать статус загрузки, но с picasso я могу предложить быстрое предложение, которое заключается в том, чтобы заглянуть в банку picasoo с jd-gui, и там вы можете найти свою ситуацию и решение   -  person Manmohan Badaya    schedule 03.12.2014


Ответы (1)


Создайте подкласс пользовательского представления, который просто переопределяет методы setImageBitmap() и setImageDrawable(). Затем вы можете создать свой собственный интерфейс прослушивателя и API регистрации в своем подклассе. Теперь, когда Пикассо устанавливает изображение, вы можете перезвонить зарегистрированным слушателям с помощью Bitmap или Drawable. Не забудьте вызвать суперкласс, чтобы изображение действительно применялось к пользовательскому представлению.

person Larry Schiefer    schedule 03.12.2014
comment
Извините, что не ответил вам на этот вопрос раньше - как-то пропустил активность по моему вопросу! В конце концов — да — это тот же вывод, к которому я пришел: создал подкласс пользовательского View, переопределил области, в которых они делали вызовы Picasso, и реализовал свои собственные обратные вызовы для моего родительского приложения. Отмечу ваш ответ как ответ, ура. - person aoemerson; 03.04.2015