0

EDIT 03/12/14: Posted for a few weeks but no activity. Is my question clear enough? Or TL;DR? Unfortunately since I'm a new member I can't offer a bounty :-(

My question can be summarised as:

  • How can I determine when a View which contains asynchronously loaded elements (such as web images) has fully loaded and rendered without resorting to a bit of a hack - see below.
  • Is there a deterministic way to find out when the View has finished loading without simply using a longish, defensive delay value for View.postDelayed()?

Please read on for the background and specifics of this question if you think you can help.

Background of question

I'm making use of a third party library within my app to attempt to do the following:

  1. Third party library asynchronously loads some data via a 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. Once the custom View has loaded and is fully rendered, I want to save this View to storage as a PNG.

My problem occurs at step 3: I am unable to deterministically detect when the custom View has finished loading so that I can save the PNG after the view has been fully rendered. Picasso, by design, loads images asynchronously into android View objects and, I think as a result, the methods I have attempted below have not worked:

  1. Use a GlobalLayoutListener to determine when the View has finished loading as suggested here.

    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.
    • I have used View.postDelayed(Runnable, delay) in onGlobalLayout() but, again, it's not possible to know how long the delay should be due to device power and network bandwidth plus it introduces a race condition of sorts.
  2. Many answers to similar questions on SO have suggested simply posting the savePNG() as a Runnable on the UI message queue. I think this works well for most scenarios but not when images are being asynchronously loaded into Views over the internet. Most the time this approach leads to attempting to save the image before the View is fully loaded and rendered. Of course, I can postDelayed but this leads to the same uncertainty over delay length and race conditions as in the previous point.

  3. I have attempted to wait for the MessageQueue.IdleHandler.queueIdle() event to do savePNG() but sadly this also occurs before all the resources have been loaded and rendered.

  4. Maintain the last time the onGlobalLayout was called and have another thread periodically check for "no updates in XXX ms" to determine that the view has settled down. This is not a great solution and could make the app appear unnecessarily slow but I could at least get it to work.

  5. I tried sub-classing the View and overriding dispatchDraw() (suggested here) and calling back to my application - same problem as onGlobalLayout - this fires multiples times before the View is fully loaded and rendered.

Wow, long post but hopefully it means I won't look so silly if there's a very obvious "gotcha" that has been staring me in the face all this time :-)

Community
  • 1
  • 1
aoemerson
  • 147
  • 9
  • After downloading complete image we apply bitmap to imageview , i think its enough to know download status but with picasso , i can suggest quick suggestion which is look into picasoo jar with jd-gui and there u can found your situation and solution – Manmohan Badaya Dec 03 '14 at 02:25

1 Answers1

0

Create a subclass of the custom View which simply overrides the setImageBitmap() and setImageDrawable() methods. You can then create your own listener interface and registration API in your subclass. Now when Picasso sets the image you can call back the registered listener(s) with the Bitmap or Drawable. Don't forget to call through to the superclass so the image is actually applied to the custom view.

Larry Schiefer
  • 15,687
  • 2
  • 27
  • 33
  • 1
    Sorry I didn't come back to you on this earlier - somehow missed the activity on my question! In the end - yes - this is the same conclusion I came to: subclassed the custom `View`, overrode the areas where they made the Picasso calls and implemented my own callbacks to my parent app. Will mark your response as the answer, cheers. – aoemerson Apr 03 '15 at 12:07