Кластер Карт Google не отображается правильно, когда изображения загружаются асинхронно - PullRequest
0 голосов
/ 20 мая 2019

Я пытаюсь реализовать кластер Google Map, следуя примерам в библиотеке android-map-utils (https://github.com/googlemaps/android-maps-utils). Разница в том, что я хочу загрузить изображения с некоторых URL-адресов. Я использую библиотеку Glide для этой задачи.

Это мой пользовательский ClusterRenderer:

class MapClusterRenderer extends DefaultClusterRenderer<MapClusterItem> {

  private static final int MAX_IMAGES_IN_CLUTER = 4;
  private static final int MIN_ITEMS_TO_RENDER_AS_CLUSTER = 2;

  private Context context;
  private int imageDimension;

  private IconGenerator itemIconGenerator;
  private ImageView itemImageView;

  private IconGenerator clusterIconGenerator;
  private ImageView clusterImageView;

  public MapClusterRenderer(Context context, GoogleMap googleMap, ClusterManager<MapClusterItem> clusterManager) {
    super(context, googleMap, clusterManager);
    this.context = context;

    imageDimension = (int) context.getResources().getDimension(R.dimen.image_xsm);

    int padding = (int) context.getResources().getDimension(R.dimen.cluster_marker_padding);
    itemImageView = new ImageView(context);
    itemImageView.setLayoutParams(new ViewGroup.LayoutParams(imageDimension, imageDimension));
    itemImageView.setPadding(padding, padding, padding, padding);
    itemIconGenerator = new IconGenerator(context);
    itemIconGenerator.setContentView(itemImageView);

    View clusterMarkerView = View.inflate(context, R.layout.item_cluster_marker, null);
    clusterImageView = clusterMarkerView.findViewById(R.id.images);
    clusterIconGenerator = new IconGenerator(context);
    clusterIconGenerator.setContentView(clusterMarkerView);
  }

  @Override
  protected void onBeforeClusterItemRendered(MapClusterItem mapClusterItem, MarkerOptions markerOptions) {
    markerOptions.visible(false);
  }

  @Override
  protected void onBeforeClusterRendered(Cluster<MapClusterItem> cluster, MarkerOptions markerOptions) {
    markerOptions.visible(false);
  }

  @Override
  protected void onClusterItemRendered(MapClusterItem mapClusterItem, Marker marker) {
    Glide.with(context)
      .load(mapClusterItem.getEstablishment().getImageUrl())
      .into(new CustomTarget<Drawable>(imageDimension, imageDimension) {
        @Override
        public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
          itemImageView.setImageDrawable(resource);
          Bitmap bitmap = itemIconGenerator.makeIcon();
          marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
          marker.setVisible(true);
        }

        @Override
        public void onLoadCleared(@Nullable Drawable placeholder) {

        }
      });
  }

  @Override
  protected void onClusterRendered(Cluster<MapClusterItem> cluster, Marker marker) {
    Thread thread = new Thread(() -> {
      List<FutureTarget<Drawable>> futures = new ArrayList<>();
      LatLngBounds.Builder boundsBuilder = new LatLngBounds.Builder();
      for (MapClusterItem mapClusterItem : cluster.getItems()) {
        if (futures.size() < MAX_IMAGES_IN_CLUTER) {
          FutureTarget<Drawable> future = Glide.with(context)
            .load(mapClusterItem.getEstablishment().getImageUrl())
            .submit(imageDimension, imageDimension);
          futures.add(future);
        }
        boundsBuilder.include(mapClusterItem.getLatLng());
      }
      List<Drawable> drawables = new ArrayList<>();
      for (FutureTarget<Drawable> future : futures) {
        try {
          drawables.add(future.get());
        } catch (ExecutionException | InterruptedException e) {
          Timber.e(e, "Error downloading establishment image. Drawing cluster without this image...");
        }
      }
      Handler handler = new Handler(Looper.getMainLooper());
      handler.post(() -> {
        MultiDrawable multiDrawable = new MultiDrawable(drawables);
        multiDrawable.setBounds(0, 0, imageDimension, imageDimension);
        clusterImageView.setImageDrawable(multiDrawable);
        Bitmap bitmap = clusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
        marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
        marker.setPosition(boundsBuilder.build().getCenter());
        marker.setVisible(true);
      });
    });
    thread.start();
  }

  @Override
  protected boolean shouldRenderAsCluster(Cluster cluster) {
    return cluster.getSize() >= MIN_ITEMS_TO_RENDER_AS_CLUSTER;
  }
}

Поскольку я использую Glide, мне нужно создать фоновый поток для асинхронной загрузки всех изображений с помощью метода Glide.submit, который возвращает объект Future.

for (MapClusterItem mapClusterItem : cluster.getItems()) {
  if (futures.size() < MAX_IMAGES_IN_CLUTER) {
    FutureTarget<Drawable> future = Glide.with(context)
      .load(mapClusterItem.getEstablishment().getImageUrl())
      .submit(imageDimension, imageDimension);
    futures.add(future);
  }
  boundsBuilder.include(mapClusterItem.getLatLng());
}

Тогда я жду всех будущих результатов, чтобы получить его пригодные для рисования.

List<Drawable> drawables = new ArrayList<>();
for (FutureTarget<Drawable> future : futures) {
  try {
    drawables.add(future.get());
  } catch (ExecutionException | InterruptedException e) {
    Timber.e(e, "Error downloading establishment image. Drawing cluster without this image...");
  }
}

Не нужно рисовать кластер в потоке пользовательского интерфейса точно так же, как в примере с android-map-utils.

Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> {
  MultiDrawable multiDrawable = new MultiDrawable(drawables);
  multiDrawable.setBounds(0, 0, imageDimension, imageDimension);
  clusterImageView.setImageDrawable(multiDrawable);
  Bitmap bitmap = clusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
  marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
  marker.setPosition(boundsBuilder.build().getCenter());
  marker.setVisible(true);
});

Проблема в том, что этот код генерирует пустой маркер кластера с установленным размером кластера.

Отладка этого кода Я видел, что изображения загружаются правильно, но когда вызывается метод MultiDrawable.draw, рисованные элементы не печатаются правильно на холсте.

Это все чертежи, загруженные в экземпляр MultiDrawable:

enter image description here

Это загружаемое растровое изображение:

enter image description here

Это холст результата:

enter image description here

Что я делаю не так? Я проверил много примеров, и я не могу найти проблему.

...