Glide: Image doesn't show inside a Maps custom info window

Created on 10 Dec 2014  Â·  21Comments  Â·  Source: bumptech/glide

Attempting to use Glide inside a custom info window for google maps ( https://developers.google.com/maps/documentation/android/infowindows#custom_info_windows ) and the image never shows. Please advise. Thanks!

Note, the images were fully downloaded and displayed in the previous activity, so it shouldn't be a network issue, right?

question

Most helpful comment

@newmanw won't necessarily loop, consider this:
_Disclaimer: I've never seen or used this API before, below is patched together based on docs_

class GlideInfoWindowAdapter implements GoogleMap.InfoWindowAdapter {
    private final Map<Marker, Bitmap> images = new HashMap<>();
    private final Map<Marker, Target<Bitmap>> targets = new HashMap<>();

    /** initiates loading the info window and makes sure the new image is used in case it changed */
    public void showInfoWindow(Marker marker) {
        Glide.clear(targets.get(marker)); // will do images.remove(marker) too
        marker.showInfoWindow(); // indirectly calls getInfoContents which will return null and start Glide load
    }
    /** use this to discard a marker to make sure all resources are freed and not leaked */
    public void remove(Marker marker) {
        images.remove(marker);
        Glide.clear(targets.remove(marker));
        marker.remove();
    }
    @Override // GoogleMap.InfoWindowAdapter
    public View getInfoContents(Marker marker) {
        Bitmap image = images.get(marker);
        if (image == null) {
            Glide.with(context).load(url).asBitmap().dontAnimate().into(getTarget(marker));
            return null; // or something indicating loading
        } else {
            ImageView iv = <however you create it>;
            iv.setImageBitmap(image);
            return iv;
        }
    }
    @Override // GoogleMap.InfoWindowAdapter
    public View getInfoWindow(Marker marker) {
        return null;
    }
    private Target<Bitmap> getTarget(Marker marker) {
        Target<Bitmap> target = targets.get(marker);
        if (target == null) {
            target = new InfoTarget(marker);
            targets.put(marker, target); // missing in original (fixed 2018 August)
        }
        return target;
    }
    private class InfoTarget extends SimpleTarget<Bitmap> {
        Marker marker;
        InfoTarget(Marker marker) {
            super(markerWidth, markerHeight); // otherwise Glide will load original sized bitmap which is huge
            this.marker = marker;
        }
        @Override // Target
        public void onLoadCleared(Drawable placeholder) {
            images.remove(marker); // clean up previous image, it became invalid
            // don't call marker.showInfoWindow() to update because this is most likely called from Glide.into()
        }
        @Override // Target
        public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
            // this prevents recursion, because Glide load only starts if image == null in getInfoContents
            images.put(marker, resource);
            // tell the maps API it can try to call getInfoContents again, this time finding the loaded image
            marker.showInfoWindow();
        }
    }
}

All 21 comments

I've never tried to do this, but for general debugging tips you can take a look at the page on the wiki: https://github.com/bumptech/glide/wiki/Debugging-and-Error-Handling. If it is a network problem you should see some log output once you enable the debug tag.

Can you paste the line you're using to load the image with Glide? What Target are you using? You may need to specify a size, which you can do using the override() method.

I tried the override to 50x50, still nothing. here is the log print out that occurs after i click on a map marker and the info window pops up:

V/GenericRequest﹕ Got onSizeReady in 0.122082 this: 1107803744
V/GenericRequest﹕ finished setup for calling load in 2.319549 this: 1107803744
V/GenericRequest﹕ Resource ready in 5.249504 size: 0.00476837158203125 fromCache: true this: 1107803744
V/GenericRequest﹕ finished onSizeReady in 8.026857999999999 this: 1107803744
V/GenericRequest﹕ finished run method in 8.240501 this: 1107803744

and I'm using the following Glide line:

Glide.with(mContext).load(imageURL).override(50,50).into(imageView);

thanks for your help.

Ok so the size isn't the issue.

Does the Bitmap get set on the View? Does the View end up with a Drawable? You can implement RequestListener and check to see if the load completes successfully. You might also want to try asBitmap() to load a Bitmap rather than a Drawable, it's possible there's some unusual interaction between the view and one of Glide's Drawables.

Thanks! so when setting as bitmap and using the request listener I was able to notice that the image loads the 2nd time around when it's cached. My solution is a little hackish, but it works.

Glide.with(mContext).load(imageURL).asBitmap().override(50,50).listener(new RequestListener<String, Bitmap>() {
    @Override
    public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) {
        e.printStackTrace();
        return false;
    }

    @Override
    public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
        if(!isFromMemoryCache) marker.showInfoWindow();
        return false;
    }
}).into(imageView);

The only difference between the load coming from cache and the load not coming from cache is that we may try to animate loads that don't come from cache (depending on your request). Try dontAnimate() and maybe you can get rid of the hacks?

dontAnimate() didn't change the result.

You need redraw the info window until the image has loaded.

I have the same issue and solved it with @StephenMilone hack, it works now, thanks buddy.
For people you got the same issue, you have to put the Glide code inside getInfoWindow() in your Adapter.

@ppamorim any chance to fix it, for instance by forcing redraw ?

I believe that this is due to the way the info window is drawn on the map. From google docs: https://developers.google.com/maps/documentation/android-api/infowindows

As mentioned in the previous section on info windows, an info window is not a live View, rather the view is rendered as an image onto the map. As a result, any listeners you set on the view are disregarded and you cannot distinguish between click events on various parts of the view. You are advised not to place interactive components — such as buttons, checkboxes, or text inputs — within your custom info window.

Typically a call to showInfoWindow again is the solution. But you cannot do this from the getInfoContents call as you will create an endless loop.

@newmanw won't necessarily loop, consider this:
_Disclaimer: I've never seen or used this API before, below is patched together based on docs_

class GlideInfoWindowAdapter implements GoogleMap.InfoWindowAdapter {
    private final Map<Marker, Bitmap> images = new HashMap<>();
    private final Map<Marker, Target<Bitmap>> targets = new HashMap<>();

    /** initiates loading the info window and makes sure the new image is used in case it changed */
    public void showInfoWindow(Marker marker) {
        Glide.clear(targets.get(marker)); // will do images.remove(marker) too
        marker.showInfoWindow(); // indirectly calls getInfoContents which will return null and start Glide load
    }
    /** use this to discard a marker to make sure all resources are freed and not leaked */
    public void remove(Marker marker) {
        images.remove(marker);
        Glide.clear(targets.remove(marker));
        marker.remove();
    }
    @Override // GoogleMap.InfoWindowAdapter
    public View getInfoContents(Marker marker) {
        Bitmap image = images.get(marker);
        if (image == null) {
            Glide.with(context).load(url).asBitmap().dontAnimate().into(getTarget(marker));
            return null; // or something indicating loading
        } else {
            ImageView iv = <however you create it>;
            iv.setImageBitmap(image);
            return iv;
        }
    }
    @Override // GoogleMap.InfoWindowAdapter
    public View getInfoWindow(Marker marker) {
        return null;
    }
    private Target<Bitmap> getTarget(Marker marker) {
        Target<Bitmap> target = targets.get(marker);
        if (target == null) {
            target = new InfoTarget(marker);
            targets.put(marker, target); // missing in original (fixed 2018 August)
        }
        return target;
    }
    private class InfoTarget extends SimpleTarget<Bitmap> {
        Marker marker;
        InfoTarget(Marker marker) {
            super(markerWidth, markerHeight); // otherwise Glide will load original sized bitmap which is huge
            this.marker = marker;
        }
        @Override // Target
        public void onLoadCleared(Drawable placeholder) {
            images.remove(marker); // clean up previous image, it became invalid
            // don't call marker.showInfoWindow() to update because this is most likely called from Glide.into()
        }
        @Override // Target
        public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
            // this prevents recursion, because Glide load only starts if image == null in getInfoContents
            images.put(marker, resource);
            // tell the maps API it can try to call getInfoContents again, this time finding the loaded image
            marker.showInfoWindow();
        }
    }
}

@TWiStErRob great answer!

Thanks @TWiStErRob !!!

@TWiStErRob thanks!

Considering the above 3 enthusiastic responses I guess my code is "good enough" to go with. Feel free to reopen/comment if you feel otherwise, or open a new issue referencing this for any further help.

why we should use marker.showInfoWindow();
if i used image_view.setImageDrawable(resource) in onResourceReady().

@pranavmoyal it needs to be async: if the image is not available when you want to show the info window it could take seconds for the image to load, the problem with that is the user is presented with nothing during that time. It's better to show the window, and then load the image and refresh the window.

@TWiStErRob Thanks Buddy....

@StephenMilone @newmanw @j1c1m1b1 @teto100 @pranavmoyal @cuiti @oNguyenVuLan @devganiyahitesh1 @balaganivenkatasubbaiah there was a bug in the original the target was never stored against marker, the targets was never populated. Added a put into getTarget.

If you have any updates/enhancements feel free to send them to me and I'll update.

@TWiStErRob Maybe update on the Glide.clear() part? I think that now you need to do it like this: Glide.with(fragment).clear(target)
It could also be "context" instead of fragment but how would you implement it here? Or this don't apply to V4?

@TWiStErRob also showInfoWindow(Marker marker) is never used??

This has really saved me!
Few changes:

private final Map<Marker, BitmapDrawable> images = new HashMap<>();
private final Map<Marker, BaseTarget<BitmapDrawable>> targets = new HashMap<>();

.......................................
BitmapDrawable image = images.get(marker);
            if (image == null) {
                GlideApp.with(activity)
                        .load(imageUrl)
                        .placeholder(mDrawable)
                        .error(mDrawable)
                        .into(getTarget(marker));
            } else {
                ivLogo.setImageDrawable(image);
            }
.................................
private BaseTarget getTarget(Marker marker) {
        //BaseTarget<BitmapDrawable>
        BaseTarget target = targets.get(marker);
        if (target == null) {
            target = new InfoTarget(marker);
            targets.put(marker, target); // missing in original (fixed 2018 August)
        }
        return target;
    }
..................................................
private class InfoTarget extends SimpleTarget<BitmapDrawable> {

        Marker marker;
        InfoTarget(Marker marker) {
            //super(markerWidth, markerHeight); // otherwise Glide will load original sized bitmap which is huge
            this.marker = marker;
        }

        @Override
        public void onResourceReady(@NonNull BitmapDrawable resource, @Nullable Transition<? super BitmapDrawable> transition) {
            images.put(marker, resource);
            // tell the maps API it can try to call getInfoContents again, this time finding the loaded image
            marker.showInfoWindow();
        }

        @Override
        public void onLoadCleared(@Nullable Drawable placeholder) {
            images.remove(marker);
        }
    }
........................................
Was this page helpful?
0 / 5 - 0 ratings