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?
Whatever you return from
getInfoContents()
, at that moment, is converted into aBitmap
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 theBitmap
is already created and used.You will need to download the image before
getInfoContents()
has been called.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 aCanvas
, 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 callshowInfoWindow()
again on theMarker
object, and that will re-render theCanvas
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 handleBitmap
s very well. After loading several bitmaps, anOutOfMemoryError
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 aMarker
in the constructor (so you can callshowInfoWindow()
on that marker again.The info window looks like this:
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 callshowInfoWindow()
and that will re-call the callback, which will recallshowInfoWindow()
... 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.
I do this, also referencing to the @Daniel Gray answer:
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.
And here is the method for calling the info window in my Main Activity where I implement the delay.