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; } }
方案说明
- 单一Adapter实例:避免重复创建Adapter导致的状态混乱,所有Marker共用同一个Adapter实例。
- Marker Tag存储数据:每个Marker的POI数据独立存储,Adapter能动态获取当前Marker对应的图片URL,解决了图片无法切换的问题。
- Glide处理图片加载:自动处理线程切换、缓存和错误,规避了手动线程操作的UI异常,同时通过回调实现图片加载后的InfoWindow刷新。
- 手动刷新InfoWindow:利用
hideInfoWindow()+showInfoWindow()触发Google Maps重新渲染静态Bitmap,确保新图片能显示出来。
内容的提问来源于stack exchange,提问作者Sakis
相关产品推荐
相关产品推荐

