CROSP
CROSP

Reputation: 4617

Custom marker icon from image URL GoogleMap API V2

I have one problem.

My goal is to load custom image of company and show it instead default marker icon.

But I have faced with the problem.

Icons are shown only if I close and open activity again.

Here is my code for adding markers to the map.

private void addMarkerToTheMap(GoogleMapInfoBundle infoBundle) {
        if (infoBundle.placeMarker) {
            LatLng latLng = new LatLng(infoBundle.company.getLatitude(), infoBundle.firm.getLongitude());
            MarkerOptions markerOptions = new MarkerOptions()
                    .title(infoBundle.company.getName())
                    .snippet(infoBundle.company.getAddress())
                    .position(latLng);
            Marker marker = mGoogleMap.addMarker(markerOptions);
            try {
                Picasso.with(this).load(NetworkUtils.getUrlOfScaledImage(infoBundle.company.getLogo(), DEFAULT_ICON_SCALE_PERCENTAGE)).into(new PicassoMarker(marker));
            } catch (IllegalArgumentException ignore) {
                // Do nothing
            }
        }

And PicassoMarker

public class PicassoMarker implements Target {
    Marker mMarker;

    public PicassoMarker(Marker marker) {
        mMarker = marker;
    }

    @Override
    public int hashCode() {
        return mMarker.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof PicassoMarker) {
            Marker marker = ((PicassoMarker) o).mMarker;
            return mMarker.equals(marker);
        } else {
            return false;
        }
    }

    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        mMarker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
    }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {

    }
}

In this case it displays default icon enter image description here.

I have tried to make it in another way like this

MarkerOptions markerOptions = new MarkerOptions()
                        .title(infoBundle.company.getName())
                        .snippet(infoBundle.company.getAddress())
                        .position(latLng);
  try {
                Picasso.with(this).load(NetworkUtils.getUrlOfScaledImage(infoBundle.company.getLogo(), DEFAULT_ICON_SCALE_PERCENTAGE)).into(new PicassoMarker(mGoogleMap, markerOptions));
            } catch (IllegalArgumentException ignore) {
                // Do nothing
            }

And add marker to the map in callback after image has been downloaded.

   @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        mMarkerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
        mGoogleMap.addMarker(mMarkerOptions);
    }

It doesn't show any marker in this case.

But in both cases if I close Activity and open it again everything will be displayed correctly with icons as it should.

I have no idea what can cause such problem. It seems GoogleMap caches icon image or something else.

I have a little experience working with GoogleMaps API, maybe I am missing something.

Hope someone can help.

Thanks.

Upvotes: 1

Views: 2383

Answers (1)

CROSP
CROSP

Reputation: 4617

The problem was not in GoogleMap or SupportMapFragment it was in Picasso, not exactly in Picasso but in my flow.

The thing you should always remember using custom Target classes is that internally Picasso uses WeakReference for storing Target objects, so if you will just create new Target directly inside Picasso call it will be destroyed by garbage collector.

Picasso provides Callback class, so you can create anonymous class instead and that you class will hold the reference.

But one thing that is I am missing Callback class that provides Bitmap in response like Target(but targets are mostly used by custom views).

So I have found such solution, it ugly, but here it is.

I have created special list for storing target objects, not to be garbaged.

  Marker marker = mGoogleMap.addMarker(markerOptions);
            mMarkerCompanyMap.put(marker, infoBundle.firm);
            try {
                PicassoMarkerTarget markerTarget = new PicassoMarkerTarget(marker, mImageLoadingCallback);
                mPicassoMarkerTargetList.add(markerTarget);
                Picasso.with(this)
                        .load(NetworkUtils.getUrlOfScaledImage(infoBundle.firm.getLogo(), DEFAULT_ICON_SCALE_PERCENTAGE))
                        .into(markerTarget);
            } catch (IllegalArgumentException ignore) {
                // Do nothing
            }

I am passing mImageLoadingCallback for having callback back to the activity from Target
And it looks like.

private Callback mImageLoadingCallback = new Callback() {
        @Override
        public void onSuccess() {
            Logger.logError("IMAGE LOADED IN CALLBACK " + mPendingImagesCount);
            if (mPendingImagesCount <= 0) {
                mPicassoMarkerTargetList.clear();
            } else {
                mPendingImagesCount--;
            }
        }

        @Override
        public void onError() {
            // You may start laughing, but just for the sake of not duplicating code
            onSuccess();
        }
    };

This callback can be used for other purposes, but now only for clear array.

And of course PicassoMarkerTarget

   @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        mMarker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
        mDownloadListenerCallback.onSuccess();
        Logger.logError("IMAGE LOADED IN PICASSO MARKER TARGET");
    }

This is ugly solution but maybe this callback can be helpful in the future.

Please, if you have idea how to make solution better - please share it. Thanks.

Upvotes: 1

Related Questions