Добавить изображение из URL-адреса в пользовательские карты InfoWindow google maps v2

Я работаю в приложении для Android. Пользователь выполняет поиск ресторанов на картах Google. В гугл карте отображаются маркеры для всего ресторана его соседа. Если он коснется маркера, появится настраиваемое окно InfoWindow. Моя проблема в том, что я не могу загрузить изображение, которое возвращается из Google Places. Я получаю правильный URL-адрес изображения, но не могу показать его в окне.

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;

            }

});

а код DownloadImage:

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();
          }
      }
    }

Наконец, когда я выполняю код и нажимаю на маркер, getInfoContents не прекращает выполнение, и значок не появляется.

Почему так случилось?


person Mixalis    schedule 21.09.2013    source источник
comment
возможный дубликат динамического содержимого в Maps V2 InfoWindow   -  person MaciejGórski    schedule 22.09.2013
comment
@Mixalis у тебя есть решение? Пожалуйста, поделитесь со мной.   -  person Fedy Venom    schedule 15.02.2015


Ответы (4)


Я создавал похожее приложение.

Прежде всего, причина, по которой ваш InfoWindow не показывает загруженное изображение, заключается в том, что MapFragment визуализирует представление в Canvas, а затем рисует его. В информационном окне вы видите не созданные вами представления, а их «изображение» или «снимок экрана». В основном вам нужно снова вызвать showInfoWindow() для объекта Marker, и это приведет к повторному рендерингу Canvas, и теперь ваше изображение станет видимым.

Однако, как говорится, по моему опыту загрузка Bitmap из URL-адреса с последующей его настройкой - не лучшее решение. Android не очень хорошо справляется с Bitmaps. После загрузки нескольких растровых изображений возникновение OutOfMemoryError исключения - лишь вопрос времени, зависящий от объема имеющейся у вас системной памяти.

Я бы рекомендовал использовать библиотеку Picasso, которая обрабатывает асинхронную загрузку и кэширование (в памяти и на диске) и делает фактическую загрузку изображения всего одной строкой (Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);). (подробнее см. http://square.github.io/picasso/)

Предыдущий ответ был хорош, за исключением того, что, по его словам, эта «задержка» на мой вкус слишком волшебна. У Picasso есть возможность использовать обратные вызовы, и я бы рекомендовал использовать это (я использую это в своем приложении).

Сначала создайте класс (он может быть внутренним для вашей деятельности), который реализует интерфейс Callback Пикассо и получает Marker в конструкторе (чтобы вы могли снова вызвать showInfoWindow() для этого маркера.

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() {}
}

Информационное окно выглядит так:

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;
    }
});

Как видите, обратный вызов довольно прост. Однако при использовании этого метода вы ДОЛЖНЫ быть осторожны, чтобы использовать обратный вызов только в первом вызове, а не в последующих вызовах (я просто вставил это not_first_time_showing_info_window, чтобы отразить идею ... у вас будет чтобы увидеть, как включить это в свою программную логику. Если вы этого не сделаете, обратный вызов Picasso вызовет showInfoWindow(), и он повторно вызовет обратный вызов, который вызовет _17 _... ну, вы можете увидеть, куда идет эта рекурсия . :)

Главное, чтобы загрузка Picasso с обратным вызовом выполнялась только один раз и при последующих вызовах без обратного вызова.

person Daniel Gray    schedule 26.02.2014
comment
тот же подход, здесь немного более чистая реализация: stackoverflow.com/a/28885430/431432 - person Chris Chen; 19.11.2015

Я решил эту проблему с помощью черной магии (она же установка задержки). Я воспользовался кешированием Пикассо и просто вызвал showInfoWindow через несколько миллисекунд после начала начальной загрузки.

Вот мой 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;
   }
}

А вот метод вызова информационного окна в моем основном действии, в котором я реализую задержку.

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;
    }
});
person Michael Alan Huff    schedule 25.02.2014
comment
Прошло много времени с момента вашего сообщения, но вы помните, зачем вы вызывали mark.showInfoWindow (); дважды внутри onMarkerClick? - person Ignacio Rubio; 22.10.2014
comment
Привет, Игнасио, я сейчас переписываю приложение, в котором использовал этот код. Моя ситуация заключалась в том, что мне нужно было загрузить изображение, прежде чем я смогу показать его пользователю. Я хотел, чтобы пользователь чувствовал, что что-то приближается, поэтому первое предупреждение в окне должно было представить окно-заполнитель, чтобы пользователь ожидал, что что-то появится через несколько секунд. Обработчик должен был отображать фотографию по прошествии среднего времени, необходимого для загрузки определенного экземпляра фотографии. Я признаю, что на самом деле это очень плохой способ сделать это. Однако мне нужно было быстро продвигаться по проекту. Я плачу свой технический долг сейчас. - person Michael Alan Huff; 03.12.2014
comment
Классный пост. Работал как шарм! Ты спас мне день - person Gaurav Arora; 01.07.2017

Все, что вы возвращаете из getInfoContents(), в этот момент преобразуется в Bitmap и используется для отображения результатов. Вы не показываете изображение до тех пор, пока загрузка не будет завершена, и к этому моменту Bitmap уже будет создан и использован.

Вам нужно будет загрузить изображение до вызова getInfoContents().

person CommonsWare    schedule 21.09.2013
comment
На самом деле я хочу обновить Infowindow после завершения загрузки. Вот так. code.google.com/p/gmaps -api-issues / issues / detail? id = 4645 Для этого я использую var refrese. Как я могу дождаться полной загрузки устройства? Я думал, что с AsyncTask загрузка была параллельной. - person Mixalis; 22.09.2013
comment
@Mixalis: пока ваш код попытается обновить информационное окно, вы просто развернетесь и снова запустите другую задачу загрузки. Вы заполняете ImageView слишком поздно (Bitmap уже был создан), и когда вы обновите информационное окно, вы создадите другое ImageView, когда вы увеличите другой экземпляр макета. Вам нужно кэшировать Bitmap, использовать его, когда он станет доступен, когда ваше информационное окно появится снова, а не запускать другую задачу. Один из способов справиться с этим - использовать кеширующую библиотеку загрузки изображений - может работать Picasso. - person CommonsWare; 22.09.2013
comment
@Mixalis: Но спасибо, что указали на эту проблему, так как описанная там техника весьма полезна. Я постараюсь написать пример приложения для этого завтра, если я смогу придумать хороший источник соответствующих изображений ... :-) - person CommonsWare; 22.09.2013
comment
ты. Жду ... неплохо было бы выложить ссылку, если образец будет опубликован - person Mixalis; 22.09.2013
comment
@Mixalis: Если я не ответил со ссылкой примерно до вторника, оставьте комментарий здесь, чтобы напомнить мне. - person CommonsWare; 22.09.2013
comment
@Mixalis: Держите: github.com/commonsguy/cw-omnibus / tree / master / MapsV2 / ImagePopups - person CommonsWare; 22.09.2013

Я делаю это, также ссылаясь на ответ @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() {}
}
person Cristian Hoyos    schedule 14.07.2016