腾讯地图SDK Android版开发 10 InfoWindow
- 前言
- 介绍
- 默认风格
- 自定义样式
- 实现方式
- 交互操作
- 播放信息窗口的动画
- 开启多窗口模式
- 相关类和接口
- 默认样式
- MarkerOptions 类
- Marker 类
- TencentMap类
- TencentMap.OnInfoWindowClickListener 接口类
- 自定义样式
- TencentMap 类
- TencentMap.InfoWindowAdapter 接口
- 示例
- 界面布局
- MapInfoWindow类
- 常量
- 成员变量
- 初始化
- 创建与移除覆盖物
- 设置属性
- 自定义样式
- 加载与移除地图
- MapInfoWindowActivity 类
- 控件响应事件
- 运行效果图
前言
前文介绍的腾讯地图添加Marker
覆盖物的使用方法,本文重点介绍如何结合Marker
使用InfoWindow
展示更多信息。
介绍
信息窗口,是依附于Marker
之上的展现元素,用于对Marker
进行详细描述。
腾讯地图SDK默认提供的InfoWindow分标题和简述两部分,开发者也可以自定义InfoWindow,满足个性化场景的要求。
主要功能有:
-
默认信息窗口的显示和隐藏
-
创建一个自定义信息窗口
-
注册信息窗口的事件监听
-
播放信息窗口的动画
-
开启多窗口模式
默认风格
-
使用
MarkerOptions
或者Marker
来开启InfoWindow
和设置信息内容。 -
当点击
Marker
的时候,将显示InfoWindow
。 -
通过
Marker
的接口显示和隐藏InfoWindow
。
自定义样式
在TencenMap
接口中,TencentMap.InfoWindowAdapter
类用来提供自定义窗口能力。
该类提供两个接口:无边框自定义View和带边框自定义View。
当配置了InfoWindowAdapter
时,MarkerOptions
中的title
和snippet
属性即失效。
实现方式
InfoWindow
有两种实现方式,开发者需要针对不同场景选择适合的类型,展示信息窗口。
Bitmap
型,通过View
布局转化成Bitmap
,然后由渲染引擎绘制在地图上展示。View
型,通过View
布局直接在地图上展示。
两者之间的对比表格:
类型 | 定制化 | 动画 | 性能 | 复杂度 |
---|---|---|---|---|
Bitmap 型 | 低 | 有限 | 高 | 低 |
View 型 | 高 | 支持 | 低 | 高 |
-
默认
InfoWindow
使用的是Bitmap
类型,该类型整体是一张图片。 -
还可以通过
MarkerOptions
指定当前Marker
的InfoWindow
使用View
类型。
交互操作
-
SDK内置了
InfoWindow
的点击事件。 -
通过
TencentMap.setOnInfoWindowClickListener
设置Marker
气泡点击事件监听。 -
当使用
View
类型时,infoWindow
的事件监听由View
来接管处理。也就是可以给View
的任何一个子控件设置点击事件。
播放信息窗口的动画
- 当使用
Bitmap
类型时,InfoWindow
的动画由引擎内部支持,仅支持有限的几种动画类型,比如:渐变、缩放、平移、旋转。 - 当使用
View
类型时,InfoWindow
的动画由View
系统支持,可由开发者自定义处理。
开启多窗口模式
- 默认情况下,多窗口模式是关闭的,不能在地图上显示多个信息窗口。
- 通过
TencentMap.enableMultipleInfowindow(boolean)
接口来开启和关闭多窗口模式。 - 开启多窗口模式之后,在多个Marker上点击,都能正常显示信息窗口。
相关类和接口
默认样式
MarkerOptions 类
- getter
类型 | 方法 | 说明 |
---|---|---|
boolean | isInfoWindowEnable () | 获取标注的InfoWindow是否可以弹出气泡 |
boolean | isViewInfowindow () | 获取此 marker 的 infowindow 是否用 view 实现 infowindow |
String | getSnippet () | 获取标注的InfoWindow(气泡)的内容 |
String | getTitle () | 获得标注的InfoWindow(气泡)的标题 |
- setter
类型 | 方法 | 说明 |
---|---|---|
MarkerOptions | infoWindowEnable (boolean enabled) | 设置标注是否可以弹出InfoWindow(气泡) |
MarkerOptions | viewInfoWindow (boolean enabled) | 设置此 marker 的 infowindow 是否用 view 实现 infowindow |
MarkerOptions | title (String s) | 设置标注的InfoWindow(气泡)的标题,如果设置了 TencentMap.setInfoWindowAdapter(TencentMap.InfoWindowAdapter) 则失效 |
MarkerOptions | snippet (String snippet) | 设置标注的InfoWindow(气泡)的内容,如果设置了 TencentMap.setInfoWindowAdapter(TencentMap.InfoWindowAdapter) 则失效 |
MarkerOptions | infoWindowAnchor (float u, float v) | 设置infowindow anchor point |
MarkerOptions | infoWindowOffset (int offsetX, int offsetY) | 设置InfoWindow的偏移,在基准(InfoWindow在Marker正上方中间处显示 —— 默认位置)上偏移 方向:向右,向下为正方向,向左,向上为负方向 |
MarkerOptions | infoWindowCollisionBy (MarkerCollisionItem… infoWindowCollisions) | 设置InfoWindow 可被碰撞的类型,默认无碰撞关系 |
Marker 类
- getter
类型 | 方法 | 说明 |
---|---|---|
boolean | isInfoWindowEnable () | 获取标注的InfoWindow是否可以弹出气泡 |
String | getSnippet () | 获取标注的InfoWindow(气泡)的内容 |
String | getTitle () | 获得标注的InfoWindow(气泡)的标题 |
- setter
类型 | 方法 | 说明 |
---|---|---|
void | setSnippet (String snippet) | 设置标注的InfoWindow(气泡)的内容,如果设置了 TencentMap.setInfoWindowAdapter(TencentMap.InfoWindowAdapter) 则失效 |
void | setTitle (String s) | 设置标注的InfoWindow(气泡)的标题,如果设置了 TencentMap.setInfoWindowAdapter(TencentMap.InfoWindowAdapter) 则失效 |
void | setInfoWindowEnable (boolean enabled) | 设置标注是否可以弹出InfoWindow(气泡) |
void | setInfoWindowAnchor (float anchorU, float anchorV) | 设置 infowindow 的锚点,默认 (0.5f, 1) |
void | setInfoWindowOffset (int offsetX, int offsetY) | 设置 infowindow 坐标偏移 |
- 显示,隐藏和更新InfoWindow
类型 | 方法 | 说明 |
---|---|---|
boolean | isInfoWindowShown () | 获取让标注InfoWindow(气泡)是否正在显示 |
void | showInfoWindow () | 让标注显示InfoWindow(气泡) |
void | hideInfoWindow () | 让标注隐藏InfoWindow(气泡) |
void | refreshInfoWindow () | 更新infowindow |
TencentMap类
类型 | 方法 | 说明 |
---|---|---|
void | enableMultipleInfowindow (boolean enable) | 设置地图是否允许多InfoWindow模式,默认是false(只允许显示一个InfoWindow) 注意:此方法需要在addMarker之前调用。 |
void | setOnTapMapViewInfoWindowHidden (boolean enable) | 点击地图其他区域时,InfoWindow是否需要隐藏 |
void | setOnInfoWindowClickListener (TencentMap.OnInfoWindowClickListener listener) | 设置Marker气泡点击事件监听接口 |
TencentMap.OnInfoWindowClickListener 接口类
/**
* 当 Marker 气泡窗口被点击时的回调
*/
public interface OnInfoWindowClickListener {
/**
* InfoWindow被点击时回调函数
* @param Marker 被点击的InfoWindow所属的 Marker
*/
void onInfoWindowClick(Marker Marker);
/**
* 当InfoWindow点击时,点击点的回调
* @param windowWidth InfoWindow的宽度
* @param windowHigh InfoWindow的高度
* @param x 点击点在InfoWindow的x坐标点
* @param y 点击点在InfoWindow的y坐标点
*/
void onInfoWindowClickLocation(int windowWidth, int windowHigh, int x, int y);
}
自定义样式
TencentMap 类
类型 | 方法 | 说明 |
---|---|---|
void | setInfoWindowAdapter (TencentMap.InfoWindowAdapter infoWindowAdapter) | 设置气泡样式接口 |
TencentMap.InfoWindowAdapter 接口
类型 | 方法 | 说明 |
---|---|---|
View | getInfoContents (Marker marker) | 自定义气泡的标题和内容 |
View | getInfoWindow (Marker marker) | 自定义整个气泡的InfoWindow |
/**
* 自定义 Marker气泡样式接口
*/
public interface InfoWindowAdapter {
/**
* 自定义整个气泡的InfoWindow
* @param Marker 当前要弹出InfoWindow的 Marker
* 每个 marker 应返回自己的 view,不同 marker 在使用相同的 view 时会报错
* @return View
*/
View getInfoWindow(Marker Marker);
/**
* 自定义气泡的标题和内容
* @param Marker 当前要弹出InfoWindow的 Marker
* 每个 marker 应返回自己的 view,不同 marker 在使用相同的 view 时会报错
* @return View
*/
View getInfoContents(Marker Marker);
}
示例
界面布局
- 布局文件
<?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.tencent.tencentmap.mapsdk.maps.TextureMapView
android:id="@+id/mapview"
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/mapview">
<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/simple_mode"
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.5"
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.5"
android:onClick="setMarkerFlag"
android:text="适配器(内容)"
android:textColor="@color/white"
android:textStyle="bold" />
</RadioGroup>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:background="@android:color/background_dark"
android:orientation="horizontal"
android:paddingHorizontal="10dp">
<CheckBox
android:id="@+id/multiple_info_window"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="setMultipleInfoWindow"
android:text="多窗口"
android:textColor="@color/white"
android:textStyle="bold" />
<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/viewType"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="false"
android:onClick="setInfoWindowType"
android:text="View类型"
android:textColor="@color/white"
android:textStyle="bold" />
<RadioButton
android:id="@+id/bitmapType"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:onClick="setInfoWindowType"
android:text="Bitmap类型"
android:textColor="@color/white"
android:textStyle="bold" />
</RadioGroup>
</LinearLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
MapInfoWindow类
- 以下是MapInfoWIndows部分代码
常量
public static final String SIMPlE_MODE = "Simple";
public static final String ADAPTER_WINDOW_MODE = "AdapterWindowMode";
public static final String ADAPTER_CONTENT_MODE = "AdapterContentMode";
public static final String VIEW_TYPE = "ViewType";
public static final String BITMAP_TYPE = "BitmapType";
成员变量
// 覆盖物列表
List<Removable> overlays = new ArrayList<>();
// 选中的状态
String selectedMode = SIMPlE_MODE;
String selectedType = BITMAP_TYPE;
初始化
initEvent();
// 点击地图其他区域时,InfoWindow是否需要隐藏
map.setOnTapMapViewInfoWindowHidden(true);
private void initEvent() {
map.setOnInfoWindowClickListener(new TencentMap.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
showToast("单击InfoWindow");
// InfoWindow被点击时回调函数
// marker.hideInfoWindow();
}
@Override
public void onInfoWindowClickLocation(int windowWidth, int windowHigh, int x, int y) {
// 当InfoWindow点击时,点击点的回调
}
});
}
创建与移除覆盖物
- 创建覆盖物是设置InfoWindow是否用View实现
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));
// 设置此marker的InfoWindow是否用View实现
boolean isView = VIEW_TYPE.equals(selectedType);
int[] icons = BubbleIcons.Number;
for (int i = 0; i < points.size(); ++i) {
// 构建Marker图标
BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(icons[i]);
// 构建MarkerOption,用于在地图上添加Marker
MarkerOptions option = new MarkerOptions(points.get(i))
.icon(bitmap)
.title("标题" + (i + 1))
.snippet("详细信息" + (i + 1))
.viewInfoWindow(isView);
// 在地图上添加Marker,并显示
Marker marker = map.addMarker(option);
overlays.add(marker);
}
}
public void removeOverlay() {
// 清除地图上所有的标注类(Marker、Polyline、Polygon,TileOverlay除外)
// map.clearAllOverlays();
// 从地图移除覆盖物
for (Removable overlay : overlays) {
if (overlay instanceof Marker) {
Marker marker = (Marker) overlay;
marker.hideInfoWindow();
}
if (!overlay.isRemoved())
overlay.remove();
}
overlays.clear();
}
设置属性
public void enableMultipleInfoWindow(boolean enable) {
// 设置地图是否允许多InfoWindow模式
map.enableMultipleInfowindow(enable);
}
public void setSelectedMode(String flag) {
selectedMode = flag;
for (Removable overlay : overlays) {
if (overlay instanceof Marker) {
Marker marker = (Marker) overlay;
// 让标注隐藏InfoWindow(气泡)
marker.hideInfoWindow();
}
}
switch (selectedMode) {
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;
}
}
public void setSelectedType(String flag) {
selectedType = flag;
removeOverlay();
addMarkers();
}
自定义样式
- 自定义布局
custom_infowindow.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorLightBlue"> <!--背景资源可以自己设置-->
<!--用于展示自定义信息窗的内容-->
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="start|center"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
WindowModeAdapter
和ContentModeAdapter
private class WindowModeAdapter implements TencentMap.InfoWindowAdapter {
@Override
public View getInfoWindow(Marker marker) {
return createCustomInfoView(marker);
}
@Override
public View getInfoContents(Marker marker) {
return null;
}
}
private class ContentModeAdapter implements TencentMap.InfoWindowAdapter {
@Override
public View getInfoWindow(Marker marker) {
return null;
}
@Override
public View getInfoContents(Marker marker) {
return createCustomInfoView(marker);
}
}
private View createCustomInfoView(Marker marker) {
View view = LayoutInflater.from(context).inflate(R.layout.custom_infowindow, null);
TextView tvTitle = view.findViewById(R.id.tv_title);
// 设置自定义信息窗的内容
tvTitle.append(marker.getTitle() + "\n" + marker.getSnippet());
return view;
}
加载与移除地图
public void onMapLoaded() {
addMarkers();
}
public void onMapDestroy() {
removeOverlay();
}
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.simple_mode)
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.setSelectedMode(flag);
}
- 设置多窗口
public void setMultipleInfoWindow(View view) {
boolean checked = ((CheckBox) view).isChecked();
int id = view.getId();
if (id == R.id.multiple_info_window) {
mapInfoWindow.enableMultipleInfoWindow(checked);
}
}
- 设置InfoWindow显示方式:View或Bitmap
public void setInfoWindowType(View view) {
boolean checked = ((RadioButton) view).isChecked();
if (!checked)
return;
int id = view.getId();
String flag;
if (id == R.id.viewType)
flag = mapInfoWindow.VIEW_TYPE;
else if (id == R.id.bitmapType)
flag = mapInfoWindow.BITMAP_TYPE;
else
return;
mapInfoWindow.setSelectedType(flag);
}
运行效果图
简单 | 适配器(窗口) | 适配器(内容) |
---|---|---|
自定义整个气泡的InfoWindow | 自定义气泡的标题和内容 |