You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Google Maps自定义信息窗口ImageView不随Marker更新的解决方法

解决Google Maps Marker自定义InfoWindow图片不更新的问题

我看了你的代码,问题主要出在这几个核心点上:

  • 重复设置InfoWindowAdapter:每次添加Marker都重新初始化Adapter,导致每个Marker绑定的是当前POI的图片URL,切换Marker时Adapter无法获取其他Marker的图片信息。
  • 子线程操作UI:Android不允许在非UI线程修改View,你在Thread里调用img.setImageBitmap(bmp)本身就违反了Android的线程规范,会导致视图更新异常。
  • InfoWindow的静态渲染特性:Google Maps会把你返回的View转换成Bitmap显示,之后对View的任何修改都不会自动刷新到界面,必须手动触发重新渲染。
  • Adapter绑定固定图片URL:CustomInfoWindow在构造时就绑定了一个固定的photo值,无法根据当前显示的Marker动态切换图片。

下面是完整的修复方案:


第一步:优化Marker添加逻辑,只初始化一次Adapter并存储POI数据

把setInfoWindowAdapter移到GoogleMap初始化的地方(比如onMapReady),不要每次添加Marker都重复设置。同时将POI对象存入Marker的Tag中,让Adapter能动态获取当前Marker的图片URL:

// 在onMapReady中初始化一次InfoWindowAdapter
@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    // 只设置一次Adapter,所有Marker共用
    mMap.setInfoWindowAdapter(new CustomInfoWindow(MapsActivity.this));
    
    // 遍历POI列表添加Marker
    for (POI placeInfo : poiList) {
        addNewMarker(placeInfo);
    }
}

// 修改addNewMarker方法
private void addNewMarker(POI placeInfo){
    try {
        String snippet = "ID: " + placeInfo.getID() + "\nCategory: "+ placeInfo.getCategory();
        mMarker = mMap.addMarker(new MarkerOptions()
               .title(placeInfo.getPOI_name())
               .position(new LatLng(placeInfo.getLatitude(), placeInfo.getLongitude()))
               .snippet(snippet));
        // 将POI对象存入Marker的Tag,方便Adapter获取图片URL
        mMarker.setTag(placeInfo);
    }catch (NullPointerException e){
        Log.e(TAG, "moveCamera: NullPointerException thrown: " + e.getMessage() );
    }
}

第二步:重构CustomInfoWindow,用图片加载库处理图片加载(推荐Glide)

首先确保你的build.gradle中添加了Glide依赖(如果没有的话):

dependencies {
    implementation 'com.github.bumptech.glide:glide:4.16.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
}

然后修改CustomInfoWindow类,移除构造中的photo参数,从Marker的Tag获取POI数据,并利用Glide的回调刷新InfoWindow:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.Marker;
import androidx.annotation.Nullable;

public class CustomInfoWindow implements GoogleMap.InfoWindowAdapter {
    private static final String TAG = "CustomInfoWindow";
    private final View window;
    private Context context;

    public CustomInfoWindow(Context context) {
        this.context = context;
        window = LayoutInflater.from(context).inflate(R.layout.custom_makrer_info, null);
    }

    private void renderWindow(Marker marker, View view){
        // 从Marker的Tag中获取对应的POI对象
        POI placeInfo = (POI) marker.getTag();
        if (placeInfo == null) return;

        // 设置标题
        String title = marker.getTitle();
        TextView tv_Title = view.findViewById(R.id.marker_title);
        if (title != null && !title.isEmpty()){
            tv_Title.setText(title);
            tv_Title.setVisibility(View.VISIBLE);
        } else {
            tv_Title.setVisibility(View.GONE);
        }

        // 设置描述
        String snippet = marker.getSnippet();
        TextView tv_Snippet = view.findViewById(R.id.marker_snippet);
        if (snippet != null && !snippet.isEmpty()){
            tv_Snippet.setText(snippet);
            tv_Snippet.setVisibility(View.VISIBLE);
        } else {
            tv_Snippet.setVisibility(View.GONE);
        }

        // 加载图片
        ImageView img = view.findViewById(R.id.marker_image);
        String photoUrl = placeInfo.getPhotos();
        if (photoUrl != null && !photoUrl.isEmpty()){
            img.setVisibility(View.VISIBLE);
            // Glide自动处理线程切换、缓存和错误,加载完成后刷新InfoWindow
            Glide.with(context)
                 .asBitmap()
                 .load(photoUrl)
                 .listener(new RequestListener<Bitmap>() {
                     @Override
                     public boolean onLoadFailed(@Nullable com.bumptech.glide.load.engine.GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
                         img.setVisibility(View.GONE);
                         return false;
                     }

                     @Override
                     public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, com.bumptech.glide.load.DataSource dataSource, boolean isFirstResource) {
                         // 图片加载完成后,重新显示InfoWindow触发渲染
                         if (marker.isInfoWindowShown()) {
                             marker.hideInfoWindow();
                             marker.showInfoWindow();
                         }
                         return false;
                     }
                 })
                 .into(img);
        } else {
            img.setVisibility(View.GONE);
        }
    }

    @Override
    public View getInfoWindow(Marker marker) {
        renderWindow(marker, window);
        return window;
    }

    @Override
    public View getInfoContents(Marker marker) {
        renderWindow(marker, window);
        return window;
    }
}

方案说明

  1. 单一Adapter实例:避免重复创建Adapter导致的状态混乱,所有Marker共用同一个Adapter实例。
  2. Marker Tag存储数据:每个Marker的POI数据独立存储,Adapter能动态获取当前Marker对应的图片URL,解决了图片无法切换的问题。
  3. Glide处理图片加载:自动处理线程切换、缓存和错误,规避了手动线程操作的UI异常,同时通过回调实现图片加载后的InfoWindow刷新。
  4. 手动刷新InfoWindow:利用hideInfoWindow()+showInfoWindow()触发Google Maps重新渲染静态Bitmap,确保新图片能显示出来。

内容的提问来源于stack exchange,提问作者Sakis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:41:24