android - personalizar - Añadir una imagen de url en InfoWindow google maps v2 personalizado
multiple infowindows google maps (4)
Cualquier cosa que devuelva de getInfoContents()
, en ese momento, se convierte en un Bitmap
y se utiliza para mostrar los resultados. No está mostrando la imagen hasta más tarde, cuando se completa la descarga, momento en el cual el Bitmap
ya se ha creado y utilizado.
Deberá descargar la imagen antes de getInfoContents()
.
Estoy trabajando en una aplicación de Android. El usuario realiza una búsqueda en google maps para restaurantes. En google map marcadores de visualización para todos los restaurantes de su vecino. Si toca un marcador, aparece una ventana de información personalizada. Mi problema es que no puedo cargar la imagen que devuelve el formulario de Google. Estoy acertando la url de la imagen pero no puedo mostrarla en la ventana.
Ventana de información
<?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>
En crear tengo esto
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;
}
});
y el código de DownloadImage es:
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();
}
}
}
Finalmente, cuando ejecuto el código y toco el marcador, getInfoContents no detiene la ejecución y el icono no aparece.
¿Por qué sucede esto?
Hago esto, también haciendo referencia a la respuesta de @Daniel Gray :
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() {}
}
He estado construyendo una aplicación similar.
En primer lugar, la razón por la que su InfoWindow no muestra la imagen descargada es porque MapFragment
convierte la vista en un Canvas
y luego la dibuja. Lo que está viendo en la ventana de información no son las vistas que creó, sino una "imagen" o "captura de pantalla" de ellas. Básicamente, debes volver a llamar a showInfoWindow()
en el objeto Marker
, y eso volverá a representar el Canvas
y tu imagen estará visible.
Sin embargo, dicho esto, en mi experiencia, cargar el Bitmap
de Bitmap
desde la URL y luego configurarlo no es la mejor solución. Android no maneja Bitmap
s muy bien. Después de cargar varios mapas de bits, una excepción OutOfMemoryError
es solo cuestión de tiempo, dependiendo de la cantidad de memoria del sistema que tenga.
Recomiendo usar la biblioteca de Picasso, que controla la descarga asíncrona y el almacenamiento en caché (en la memoria y el disco) y hace que la imagen real se cargue solo una línea ( Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
). (más información en http://square.github.io/picasso/ )
La respuesta anterior fue buena, excepto que, como él dijo, ese "retraso" es un poco demasiado mágico para mi gusto. Picasso tiene la opción de usar devoluciones de llamada, y yo recomendaría usar eso (lo estoy usando en mi aplicación).
Primero cree una clase (puede ser interna a su actividad) que implemente la interfaz de Callback
de Callback
de Picasso, y reciba un Marker
en el constructor (para que pueda llamar a showInfoWindow()
en ese marcador nuevamente.
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() {}
}
La ventana de información se ve así:
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;
}
});
La devolución de llamada es bastante simple, como se puede ver. Sin embargo, cuando use este método, DEBE tener cuidado de usar solo la devolución de llamada en la primera llamada, y no en las llamadas subsiguientes (solo puse esa not_first_time_showing_info_window
para reflejar la idea ... tendrá que ver cómo incluirla en la lógica de tu programa. Si no lo haces, la devolución de llamada de Picasso llamará a showInfoWindow()
y volverá a llamar a la devolución de llamada, lo que recordará a showInfoWindow()
... bueno, puedes ver a dónde va esa recursión. :)
Lo principal es conseguir que la carga de Picasso con la devolución de llamada se ejecute solo una vez, y en las llamadas posteriores, sin la devolución de llamada.
Resolví este problema usando magia negra (también conocida como configuración de un retraso). Aproveché el almacenamiento en caché de Picasso y llamé a showInfoWindow unos pocos milisegundos después de que la carga inicial hubiera comenzado.
Aquí está mi 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;
}
}
Y aquí está el método para llamar a la ventana de información en mi actividad principal donde implemento el retraso.
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;
}
});