Add an Image from url into custom InfoWindow google maps v2
Asked Answered
G

4

18

I'm am working in an android app. The user make a search at google maps for restaurants. In google map display markers for all of his neighbor's restaurant. If he tap at a marker it show up a custom InfoWindow. My problem is that I can't load the image that return form Google places. Im getting right the url of image but I can't show it at Window.

InfoWindow

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/bg_color" >

<ImageView
        android:id="@+id/place_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:focusable="false"" />

<TextView
    android:id="@+id/place_title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<TextView
    android:id="@+id/place_vicinity"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<LinearLayout 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:background="@color/bg_color" >

    <RatingBar
         android:id="@+id/place_rating"
         style="?android:attr/ratingBarStyleSmall"
         android:numStars="5"
         android:rating="0"
         android:isIndicator="false"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dip" />

    <ImageView
        android:id="@+id/navigate_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:focusable="false"
        android:src="@drawable/navigate" />

</LinearLayout>

On create i have this

mGoogleMap.setInfoWindowAdapter(new InfoWindowAdapter() {

            // Use default InfoWindow frame
            @Override
            public View getInfoWindow(Marker arg0) {
                return null;
            }

            // Defines the contents of the InfoWindow
            @Override
            public View getInfoContents(Marker arg0) {

                // Getting view from the layout file info_window_layout
                View v = getLayoutInflater().inflate(R.layout.info_window_layout, null);

                // Getting the snippet from the marker
                String snippet = arg0.getSnippet();

                // Getting the snippet from the marker
                String titlestr = arg0.getTitle();

                String cutchar1= "%#";
                String cutchar2= "%##";
                String ratingstr = snippet.substring(0,snippet.indexOf( cutchar1 ));
                String vicinitystr = snippet.substring(snippet.indexOf( cutchar1 )+2, snippet.indexOf( cutchar2 ) );
                String iconurl= snippet.substring(snippet.indexOf( cutchar2 )+3);

                // Getting reference to the TextView to set latitude
                TextView title = (TextView) v.findViewById(R.id.place_title);

                TextView vicinity = (TextView) v.findViewById(R.id.place_vicinity);

                ImageView image = (ImageView) v.findViewById(R.id.navigate_icon);

                // Setting the latitude
                title.setText(titlestr);

                // declare RatingBar object
                RatingBar rating=(RatingBar) v.findViewById(R.id.place_rating);// create RatingBar object
                if( !(ratingstr.equals("null")) ){
                    rating.setRating(Float.parseFloat(ratingstr));
                }
                vicinity.setText(vicinitystr);                  

                final DownloadImageTask download = new DownloadImageTask((ImageView) v.findViewById(R.id.place_icon) ,arg0);
                download.execute(iconurl);
                // Returning the view containing InfoWindow contents
                return v;

            }

});

and the DownloadImage code is:

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
      ImageView bmImage;
      Marker marker;
      boolean refresh;

      public DownloadImageTask(final ImageView bmImage, final Marker marker) {
          this.bmImage = bmImage;
          this.marker=marker;
          this.refresh=false;
      }

     public void SetRefresh(boolean refresh ){
         this.refresh=true;

     }

    /*  @Override
      protected void onPreExecute() 
      {
          super.onPreExecute();
          bmImage.setImageBitmap(null);
      }*/

      @Override
      protected Bitmap doInBackground(String... urls) {
          String urldisplay = urls[0];
          Bitmap mIcon11 = null;
          try {
            InputStream in = new java.net.URL(urldisplay).openStream();
            mIcon11 = BitmapFactory.decodeStream(in);
          } catch (Exception e) {
              Log.e("Error", e.getMessage());
              e.printStackTrace();
          }
          return mIcon11;
      }
      @Override
      protected void onPostExecute(Bitmap result) {
          if(!refresh){
              SetRefresh(refresh);
              bmImage.setImageBitmap(result);
              marker.showInfoWindow();
          }
      }
    }

Finally when I execute the code and tap the marker the getInfoContents doesn't stop execute and the icon does not appear.

Why this happen?

Gradualism answered 21/9, 2013 at 22:9 Comment(2)
possible duplicate of Dynamic contents in Maps V2 InfoWindowSubtitle
@Gradualism have you got the solution? Please share it to me.Corneliacornelian
M
38

I've been building a similar app.

First of all, the reason your InfoWindow is not showing the downloaded image is because the MapFragment renders the view into a Canvas, and then draws that. What you're seeing in the info window aren't the views you created, but a "picture" or "screenshot" of them. You basically need to call showInfoWindow() again on the Marker object, and that will re-render the Canvas and your image will now be visible.

However, that being said, in my experience loading the Bitmap from the URL and then setting it isn't the best solution. Android doesn't handle Bitmaps very well. After loading several bitmaps, an OutOfMemoryError exception is just a matter of time, depending on the amount of system memory you have.

I'd recommend using the Picasso library, which handles the asynchronous downloading, and caching (in memory and disk) and makes the actual image loading just one line (Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);). (more info at http://square.github.io/picasso/)

The previous answer was good, except that as he said, that "delay" is a little bit too magical for my taste. Picasso has the option of using callbacks, and I'd recommend using that (I'm using that in my app).

First create a class (it can be internal to your activity) that implements Picasso's Callback interface, and receives a Marker in the constructor (so you can call showInfoWindow() on that marker again.

private class InfoWindowRefresher implements Callback {
   private Marker markerToRefresh;

   private InfoWindowRefresher(Marker markerToRefresh) {
        this.markerToRefresh = markerToRefresh;
    }

    @Override
    public void onSuccess() {
        markerToRefresh.showInfoWindow();
    }

    @Override
    public void onError() {}
}

The info window looks like this:

mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
    @Override
    public View getInfoWindow(Marker marker) {
        // inflate view window

        // set other views content

        // set image view like this:
        if (not_first_time_showing_info_window) {
            Picasso.with(ActivityClass.this).load(restaurantPictureURL).into(imgInfoWindowPicture);
        } else { // if it's the first time, load the image with the callback set
            not_first_time_showing_info_window=true;
            Picasso.with(ActivityClass.this).load(restaurantPictureURL).into(imgInfoWindowPicture,new InfoWindowRefresher(marker));
        }

        return v;
    }

    @Override
    public View getInfoContents(Marker marker) {
        return null;
    }
});

The callback is pretty simple, as you can see. However, when using this method you MUST be careful to only use the callback in the first call, and not in subsequent calls (I just put in that not_first_time_showing_info_window to reflect the idea... you'll have to see how to include that in your program logic. If you don't do that, the Picasso callback will call showInfoWindow() and that will re-call the callback, which will recall showInfoWindow()... well, you can see where that recursion's going. :)

The main thing is getting the Picasso load with the callback to only run once, and on the subsequent calls, without the callback.

Metz answered 26/2, 2014 at 14:5 Comment(1)
same approach, a bit cleaner implementation here: https://mcmap.net/q/668921/-image-not-loading-from-url-in-custom-infowindow-using-picasso-image-loading-library-in-androidBifrost
B
11

I solved this problem using black magic (aka setting a delay). I took advantage of Picasso's caching and just called showInfoWindow a few milliseconds after the initial loading had began.

Here is my CustomWindowAdapter.

class CustomWindowAdapter implements InfoWindowAdapter{
   LayoutInflater mInflater;
   Map<Marker, String> imageStringMapMarker;
   Context context;

   public CustomWindowAdapter(LayoutInflater i,  Map<Marker, String> imageStringMapMarker2, Context context ){
      mInflater = i;
      imageStringMapMarker = imageStringMapMarker2;
   }

   @Override
   public View getInfoContents(final Marker marker) {

      View v = mInflater.inflate(R.layout.custom_info_window, null);

      ImageView ivThumbnail = (ImageView) v.findViewById(R.id.ivThumbnail);
      String urlImage = imageStringMapMarker.get(marker).toString();
      Picasso.with(context).load(Uri.parse(urlImage)).resize(250,250).into(ivThumbnail);

      return v;

   }

   @Override
   public View getInfoWindow(Marker marker) {
    // TODO Auto-generated method stub
     return null;
   }
}

And here is the method for calling the info window in my Main Activity where I implement the delay.

myMap.setInfoWindowAdapter(new CustomWindowAdapter(this.getLayoutInflater(),
imageStringMapMarker, getApplicationContext()));
myMap.setOnMarkerClickListener(new OnMarkerClickListener() {

    @Override
    public boolean onMarkerClick(final Marker mark) {


    mark.showInfoWindow();

        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mark.showInfoWindow();

            }
        }, 200);

        return true;
    }
});
Billie answered 25/2, 2014 at 9:17 Comment(3)
It has been a long time since your post, but do you remember why did you put the call to "mark.showInfoWindow();" twice inside "onMarkerClick"?Battleplane
Hey Ignacio, I'm currently re-writing the application where I used this code. My situation was that I needed to download the picture before I could show it to the user. I wanted to user to have a sense that something was coming, so the first window alert was to present the placeholder window so the user would expect something to appear in a few moments. The handler was to display the photo after the average time it took to load a particular instance of a photo. I acknowledge that is actually a very bad way to do it. However, I needed to move fast on the project. I'm paying my tech debt now.Billie
Awesome post. Worked like a charm! You saved my dayOdense
H
7

Whatever you return from getInfoContents(), at that moment, is converted into a Bitmap and is used for displaying the results. You are not displaying the image until later, when the download is complete, by which point in time the Bitmap is already created and used.

You will need to download the image before getInfoContents() has been called.

Heartbreak answered 21/9, 2013 at 23:1 Comment(6)
Actually i want to refresh Infowindow after complete the download. Like this.code.google.com/p/gmaps-api-issues/issues/detail?id=4645 For that i use var refrese . How i can wait unitll complete download? I thought that with AsyncTask the download did parallel.Gradualism
@Mixalis: While your code will attempt to refresh the info window, you then just turn around and start another download task again. You are populating an ImageView too late (the Bitmap was already created), and when you refresh the info window, you will create another different ImageView when you inflate another instance of the layout. You need to cache the Bitmap, use it when it is available when your info window appears again, and not start another task. One way to handle that is to use a caching image loading library -- Picasso may work.Heartbreak
@Mixalis: But thanks for pointing out that issue, as the technique described there is rather helpful. I will see about writing a sample app for this tomorrow, assuming I can think of a good source of relevant images... :-)Heartbreak
ty. I am waiting for it... it will be nice to post link if the sample be publishGradualism
@Mixalis: If I have not responded with a link by Tuesday or so, comment back here to remind me.Heartbreak
@Mixalis: Here you go: github.com/commonsguy/cw-omnibus/tree/master/MapsV2/ImagePopupsHeartbreak
J
1

I do this, also referencing to the @Daniel Gray answer:

if (userImg.getDrawable() == null) {
  Picasso.with(ctx).load(UtilitiesApp.urlServer + user.getImgUrl())
      .error(R.drawable.logo)
      .into(userImg, new InfoWindowRefresher(marker));
} else {
  Picasso.with(ctx).load(UtilitiesApp.urlServer + user.getImgUrl())
      .error(R.drawable.logo)
      .into(userImg);
}


public class InfoWindowRefresher implements Callback {
  private Marker markerToRefresh;

  public InfoWindowRefresher(Marker markerToRefresh) {
    this.markerToRefresh = markerToRefresh;
  }

  @Override
  public void onSuccess() {
    markerToRefresh.showInfoWindow();
  }

  @Override
  public void onError() {}
}
Johen answered 14/7, 2016 at 20:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.