高德地图SDK Android版开发 10 InfoWindow
- 前言
- 相关类和方法
- 默认样式
- Marker类
- AMap类
- AMap.OnInfoWindowClickListener 接口
- 自定义样式(视图)
- AMap 类
- AMap.ImageInfoWindowAdapter 接口
- 自定义样式(Image)
- AMap.ImageInfoWindowAdapter 接口
- 示例
- 界面布局
- MapInfoWindow类
- 常量
- 成员变量
- 初始化
- 创建与移除覆盖物
- 设置属性
- 自定义样式
- 加载与移除地图
- MapInfoWindowActivity 类
- 控件响应事件
- 运行效果图
前言
前文介绍高德地图添加Marker
覆盖物的使用方法,Marker
结合InfoWindow
可展示更详尽的信息。本文将介绍以下内容:
- 使用SDK默认样式显示
InfoWindow
的方法; - 自定义
InfoWindow
样式的方法。
相关类和方法
默认样式
- 在高德地图SDK中,
InfoWindow
是点标记的一部分,默认的Infowindow
只显示Marker
对象的两个属性,一个是 title 和另一个 snippet。 - SDK 为用户提供了默认的
InfoWindow
样式,调用Marker
类的showInfoWindow()
和hideInfoWindow()
方法可以控制显示和隐藏。 - 当改变
Marker
的title
和snippet
属性时,再次调用showInfoWindow()
,可以更新InfoWindow
显示内容。
Marker类
- 标题、文字片段和附加信息的方法
类型 | 方法 | 说明 |
---|---|---|
String | getSnippet () | 获取Marker 覆盖物的文字片段。 |
String | getTitle () | 获取Marker 覆盖物的标题。 |
Object | getObject () | 获取Marker覆盖物的附加信息对象,即自定义的Marker的属性。 |
void | setSnippet (String snippet) | 设置Marker 覆盖物的文字片段。 |
void | setTitle (String title) | 设置Marker 覆盖物的标题。 |
void | setObject (Object object) | 设置Marker覆盖物的附加信息对象。 |
- InfoWindow的方法
类型 | 方法 | 说明 |
---|---|---|
boolean | isInfoWindowEnable () | 获取Marker覆盖物是否允许InfoWindow显示, 可以通过 Marker.setInfoWindowEnable(boolean) 进行设置 |
void | setInfoWindowEnable (boolean enabled) | 设置Marker覆盖物的InfoWindow是否允许显示,默认为true。 设置为false之后, 调用Marker.showInfoWindow() 将不会生效 |
boolean | isInfoWindowShown () | 返回Marker覆盖物的信息窗口是否显示,true: 显示,false: 不显示。 |
void | showInfoWindow () | 显示 Marker 覆盖物的信息窗口。 |
void | hideInfoWindow () | 隐藏Marker覆盖物的信息窗口。 |
AMap类
类型 | 方法 | 说明 |
---|---|---|
void | setOnInfoWindowClickListener (AMap.OnInfoWindowClickListener listener) | 设置marker的信息窗口点击事件监听接口。 |
AMap.OnInfoWindowClickListener 接口
public interface OnInfoWindowClickListener {
void onInfoWindowClick(Marker marker);
}
自定义样式(视图)
AMap 类
类型 | 方法 | 说明 |
---|---|---|
void | setInfoWindowAdapter (AMap.InfoWindowAdapter adapter) | 设置marker的信息窗口定制接口。 |
AMap.ImageInfoWindowAdapter 接口
用来定制Marker
的信息窗口。
类型 | 方法 | 说明 |
---|---|---|
android.view.View | getInfoWindow (Marker marker) | 定制展示marker信息的View。(注:可自定义背景) |
android.view.View | getInfoContents (Marker marker) | 定制展示marker信息的View。(注:使用默认背景) |
public interface InfoWindowAdapter {
// 如果返回的View不为空且View的background不为null,则直接使用它来展示marker的信息。
// 如果backgound为null,SDK内部会给这个View设置一个默认的background。
// 如果这个方法返回null,内容将会从getInfoContents(Marker)方法获取。
View getInfoWindow(Marker marker);
// 如果返回的View不为空且View的background不为null,则直接使用它来展示marker的信息。
// 如果backgound为null,SDK内部会给这个View设置一个默认的background。
// 如果这个方法返回null,将使用内置的一个默认的View来展示marker的信息。
View getInfoContents(Marker marker);
}
触发机制
- 默认情况下,当单击某个marker时,如果该marker的Title和Snippet不为空,则会触发getInfoWindow和getInfoContents回调。
- 另外,通过**调用Marker.showInfoWindow()**同样可以触发上面两个回调。
返回null的处理逻辑
- 自5.2.1开始,如果getInfoWindow(Marker) 和 getInfoContents(Marker) 均返回null,将不展示InfoWindow的信息
自定义样式(Image)
说明:此方法官方指南未介绍,来自参考手册。(未做验证)
AMap.ImageInfoWindowAdapter 接口
用途:
用来实现marker与对应InfoWindow同步移动。
默认情况下,InfoWindow是一个View, 拖动地图的时候由于View 布局较慢,会有延迟的效果。
为了解决此问题,新增AMap.ImageInfoWindowAdapter, InfoWindow会被转为图片,拖动地图时会跟随Marker
注意
使用
ImageInfoWindowAdapter
后InfoWindow作为View本身的功能被减弱,比如动态更新图片,播放Gif图片等等均无法使用。如果想要动态的去更新infowindow内容,请务必仔细看看此接口的更新机制。
更新机制
设置此接口返回值之后,会定期(默认周期无穷大)调用一个
getInfoWindow(Marker)
并将View转换为图片。由于将View转成图片会比较耗时,不能一直调用,而设置时间间隔可以减少一定的耗时。
调用
Marker.showInfoWindow()
也可以触发调用AMap.InfoWindowAdapter.getInfoWindow(Marker)
并将View转换为图片。
类型 | 方法 | 说明 |
---|---|---|
long | getInfoWindowUpdateTime() | 自定义整个信息窗口属性间隔时间。单位为 ms |
方法说明
- 如果返回值 小于或等于 0,则认为是无穷大。
- 如果返回值 (0,100] , 则认为是100(如果频繁将View转成图片,内存抖动会很严重,建议这个值不要太低)。
- 如果这个想实现更小的时间间隔或者不想受这个接口约束,可以保持返回默认值,并自行设置计时器。
示例
界面布局
- 布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="MapInfoWindowActivity">
<com.amap.api.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/bottomView"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/bottomView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/map">
<RadioGroup
android:id="@+id/RadioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/background_dark"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:paddingHorizontal="10dp">
<RadioButton
android:id="@+id/simpleMode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:onClick="setMarkerFlag"
android:text="简单"
android:textColor="@color/white"
android:textStyle="bold" />
<RadioButton
android:id="@+id/adapter_window_mode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="setMarkerFlag"
android:text="适配器(窗口)"
android:textColor="@color/white"
android:textStyle="bold" />
<RadioButton
android:id="@+id/adapter_content_mode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="setMarkerFlag"
android:text="适配器(内容)"
android:textColor="@color/white"
android:textStyle="bold" />
</RadioGroup>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
MapInfoWindow类
- 以下是MapInfoWIndows部分代码
常量
public static final String SIMPlE_MODE = "SimpleMode";
public static final String ADAPTER_WINDOW_MODE = "AdapterWindowMode";
public static final String ADAPTER_CONTENT_MODE = "AdapterContentMode";
成员变量
// 覆盖物列表
List<BaseOverlay> overlays = new ArrayList<>();
// 选中的状态
String selectedFlag = SIMPlE_MODE;
// 气泡图标
ArrayList<BitmapDescriptor> bitmaps = new ArrayList<>();
初始化
int[] drawableIds = BubbleIcons.Number;
for (int drawableId : drawableIds) {
BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(drawableId);
bitmaps.add(bitmap);
}
initEvent();
private void initEvent() {
// 设置marker的信息窗口点击事件监听接口。
map.setOnInfoWindowClickListener(new AMap.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
// 隐藏Marker覆盖物的信息窗口。
marker.hideInfoWindow();
}
});
}
创建与移除覆盖物
public void addMarkers() {
// 构造大量坐标数据
List<LatLng> points = new ArrayList<>();
points.add(new LatLng(39.97923, 116.357428));
points.add(new LatLng(39.94923, 116.397428));
points.add(new LatLng(39.97923, 116.437428));
points.add(new LatLng(39.92353, 116.490705));
points.add(new LatLng(40.023537, 116.289429));
points.add(new LatLng(40.022211, 116.406137));
// 创建OverlayOptions的集合
ArrayList<MarkerOptions> optionsList = new ArrayList<>();
for (int i = 0; i < points.size(); ++i) {
// 创建OverlayOptions属性
MarkerOptions option = new MarkerOptions()
.position(points.get(i))
.icon(bitmaps.get(i))
.title("标题" + (i + 1))
.snippet("详细信息" + (i + 1));
// 将OverlayOptions添加到list
optionsList.add(option);
}
boolean moveToCenter = true;
// 在地图上添一组图片标记(marker)对象,
// 并设置是否改变地图状态以至于所有的marker对象都在当前地图可视区域范围内显示。
ArrayList<Marker> newOverlays = map.addMarkers(optionsList, moveToCenter);
overlays.addAll(newOverlays);
}
public void removeOverlay() {
// 从地图上删除所有的覆盖物(marker,circle,polyline 等对象),
// 但myLocationOverlay(内置定位覆盖物)除外。
// boolean isKeepMyLocationOverlay = true;
// map.clear(isKeepMyLocationOverlay);
for (BaseOverlay overlay : overlays) {
if (overlay instanceof Marker) {
Marker marker = (Marker) overlay;
marker.hideInfoWindow();
}
}
overlays.clear();
}
设置属性
public void setFlag(String flag) {
selectedFlag = flag;
switch (selectedFlag) {
case SIMPlE_MODE:
map.setInfoWindowAdapter(null);
break;
case ADAPTER_WINDOW_MODE:
map.setInfoWindowAdapter(new WindowModeAdapter());
break;
case ADAPTER_CONTENT_MODE:
map.setInfoWindowAdapter(new ContentModeAdapter());
break;
}
}
说明:自定义样式参考官方Demo,WindowModeAdapter
和ContentModeAdapter
为自定义两个适配器。代码和布局见附录。
自定义样式
- WindowModeAdapter
布局custom_info_window.xml
(background
+image
+tItle
+snippet
)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/custom_info_bubble"
android:orientation="horizontal">
<ImageView
android:id="@+id/badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#ff000000"
android:textSize="14dp"
android:textStyle="bold" />
<TextView
android:id="@+id/snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#ff7f7f7f"
android:textSize="14dp" />
</LinearLayout>
</LinearLayout>
WindowModeAdapter
类
private class WindowModeAdapter implements AMap.InfoWindowAdapter {
@Override
public View getInfoWindow(Marker marker) {
// 加载自定义布局文件作为InfoWindow的样式
View view = LayoutInflater.from(context).inflate(R.layout.custom_info_window, null);
render(marker, view);
return view;
}
@Override
public View getInfoContents(Marker marker) {
return null;
}
}
- ContentModeAdapter
布局custom_info_contents.xml
(image
+tItle
+snippet
)
<?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="horizontal">
<ImageView
android:id="@+id/badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:adjustViewBounds="true" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#ff000000"
android:textSize="14dp"
android:textStyle="bold" />
<TextView
android:id="@+id/snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#ff7f7f7f"
android:textSize="14dp" />
</LinearLayout>
</LinearLayout>
ContentModeAdapter
类
private class ContentModeAdapter implements AMap.InfoWindowAdapter {
@Override
public View getInfoWindow(Marker marker) {
return null;
}
@Override
public View getInfoContents(Marker marker) {
// 加载自定义布局文件作为InfoWindow的样式
View view = LayoutInflater.from(context).inflate(R.layout.custom_info_contents, null);
render(marker, view);
return view;
}
}
- render方法(设置
image
+title
+snippet
)
private void render(Marker marker, View view) {
ImageView imageView = view.findViewById(R.id.badge);
imageView.setImageResource(android.R.drawable.ic_menu_gallery);
String title = marker.getTitle();
TextView titleUi = view.findViewById(R.id.title);
if (title != null) {
SpannableString titleText = new SpannableString(title);
titleText.setSpan(new ForegroundColorSpan(Color.RED), 0, titleText.length(), 0);
titleUi.setTextSize(15);
titleUi.setText(titleText);
} else {
titleUi.setText("");
}
String snippet = marker.getSnippet();
TextView snippetUi = view.findViewById(R.id.snippet);
if (snippet != null) {
SpannableString snippetText = new SpannableString(snippet);
snippetText.setSpan(new ForegroundColorSpan(Color.GREEN), 0, snippetText.length(), 0);
snippetUi.setTextSize(20);
snippetUi.setText(snippetText);
} else {
snippetUi.setText("");
}
}
加载与移除地图
public void onMapLoaded() {
addMarkers();
setFlag(SIMPlE_MODE);
}
public void onMapDestroy() {
removeOverlay();
for (BitmapDescriptor bitmap : bitmaps) {
bitmap.recycle();
}
bitmaps = null;
}
MapInfoWindowActivity 类
- 以下是MapInfoWindowActivity类部分代码
控件响应事件
public void setMarkerFlag(View view) {
boolean checked = ((RadioButton) view).isChecked();
if (!checked)
return;
int id = view.getId();
String flag;
if (id == R.id.simpleMode)
flag = MapInfoWindow.SIMPlE_MODE;
else if (id == R.id.adapter_window_mode)
flag = MapInfoWindow.ADAPTER_WINDOW_MODE;
else if (id == R.id.adapter_content_mode)
flag = MapInfoWindow.ADAPTER_CONTENT_MODE;
else
return;
mapInfoWindow.setFlag(flag);
}
运行效果图
简单 | 适配器(窗口) | 适配器(内容) |
---|---|---|
默认样式 | 自定义:background +image +title +snippet | 自定义:image +title +snippet |