前言
前文介紹高德地圖添加Marker
覆蓋物的使用方法氮块,Marker
結(jié)合InfoWindow
可展示更詳盡的信息宏榕。本文將介紹以下內(nèi)容:
- 使用SDK默認(rèn)樣式顯示
InfoWindow
的方法棠赛; - 自定義
InfoWindow
樣式的方法。
相關(guān)類和方法
默認(rèn)樣式
- 在高德地圖SDK中器虾,
InfoWindow
是點標(biāo)記的一部分朵锣,默認(rèn)的Infowindow
只顯示Marker
對象的兩個屬性捞高,一個是 title 和另一個 snippet氯材。 - SDK 為用戶提供了默認(rèn)的
InfoWindow
樣式,調(diào)用Marker
類的showInfoWindow()
和hideInfoWindow()
方法可以控制顯示和隱藏硝岗。 - 當(dāng)改變
Marker
的title
和snippet
屬性時氢哮,再次調(diào)用showInfoWindow()
,可以更新InfoWindow
顯示內(nèi)容型檀。
Marker類
- 標(biāo)題冗尤、文字片段和附加信息的方法
類型 | 方法 | 說明 |
---|---|---|
String |
getSnippet () |
獲取Marker 覆蓋物的文字片段。 |
String |
getTitle () |
獲取Marker 覆蓋物的標(biāo)題胀溺。 |
Object |
getObject () |
獲取Marker覆蓋物的附加信息對象裂七,即自定義的Marker的屬性。 |
void |
setSnippet (String snippet) |
設(shè)置Marker 覆蓋物的文字片段仓坞。 |
void |
setTitle (String title) |
設(shè)置Marker 覆蓋物的標(biāo)題背零。 |
void |
setObject (Object object) |
設(shè)置Marker覆蓋物的附加信息對象。 |
- InfoWindow的方法
類型 | 方法 | 說明 |
---|---|---|
boolean |
isInfoWindowEnable () |
獲取Marker覆蓋物是否允許InfoWindow顯示, 可以通過 Marker.setInfoWindowEnable(boolean) 進(jìn)行設(shè)置 |
void |
setInfoWindowEnable (boolean enabled) |
設(shè)置Marker覆蓋物的InfoWindow是否允許顯示,默認(rèn)為true无埃。 設(shè)置為false之后, 調(diào)用Marker.showInfoWindow() 將不會生效 |
boolean |
isInfoWindowShown () |
返回Marker覆蓋物的信息窗口是否顯示捉兴,true: 顯示蝎困,false: 不顯示。 |
void |
showInfoWindow () |
顯示 Marker 覆蓋物的信息窗口倍啥。 |
void |
hideInfoWindow () |
隱藏Marker覆蓋物的信息窗口。 |
AMap類
類型 | 方法 | 說明 |
---|---|---|
void |
setOnInfoWindowClickListener (AMap.OnInfoWindowClickListener listener) |
設(shè)置marker的信息窗口點擊事件監(jiān)聽接口澎埠。 |
AMap.OnInfoWindowClickListener 接口
public interface OnInfoWindowClickListener {
void onInfoWindowClick(Marker marker);
}
自定義樣式(視圖)
AMap 類
類型 | 方法 | 說明 |
---|---|---|
void |
setInfoWindowAdapter (AMap.InfoWindowAdapter adapter) |
設(shè)置marker的信息窗口定制接口虽缕。 |
AMap.ImageInfoWindowAdapter 接口
用來定制Marker
的信息窗口。
類型 | 方法 | 說明 |
---|---|---|
android.view.View |
getInfoWindow (Marker marker) |
定制展示marker信息的View蒲稳。(注:可自定義背景) |
android.view.View |
getInfoContents (Marker marker) |
定制展示marker信息的View氮趋。(注:使用默認(rèn)背景) |
public interface InfoWindowAdapter {
// 如果返回的View不為空且View的background不為null,則直接使用它來展示marker的信息江耀。
// 如果backgound為null剩胁,SDK內(nèi)部會給這個View設(shè)置一個默認(rèn)的background。
// 如果這個方法返回null祥国,內(nèi)容將會從getInfoContents(Marker)方法獲取昵观。
View getInfoWindow(Marker marker);
// 如果返回的View不為空且View的background不為null,則直接使用它來展示marker的信息舌稀。
// 如果backgound為null啊犬,SDK內(nèi)部會給這個View設(shè)置一個默認(rèn)的background。
// 如果這個方法返回null壁查,將使用內(nèi)置的一個默認(rèn)的View來展示marker的信息觉至。
View getInfoContents(Marker marker);
}
觸發(fā)機(jī)制
- 默認(rèn)情況下,當(dāng)單擊某個marker時睡腿,如果該marker的Title和Snippet不為空语御,則會觸發(fā)getInfoWindow和getInfoContents回調(diào)。
- 另外席怪,通過調(diào)用Marker.showInfoWindow()同樣可以觸發(fā)上面兩個回調(diào)应闯。
返回null的處理邏輯
- 自5.2.1開始,如果getInfoWindow(Marker) 和 getInfoContents(Marker) 均返回null何恶,將不展示InfoWindow的信息
自定義樣式(Image)
說明:此方法官方指南未介紹孽锥,來自參考手冊。(未做驗證)
AMap.ImageInfoWindowAdapter 接口
用途:
用來實現(xiàn)marker與對應(yīng)InfoWindow同步移動细层。
默認(rèn)情況下惜辑,InfoWindow是一個View, 拖動地圖的時候由于View 布局較慢疫赎,會有延遲的效果盛撑。
為了解決此問題,新增AMap.ImageInfoWindowAdapter, InfoWindow會被轉(zhuǎn)為圖片捧搞,拖動地圖時會跟隨Marker
注意
使用
ImageInfoWindowAdapter
后InfoWindow作為View本身的功能被減弱抵卫,比如動態(tài)更新圖片狮荔,播放Gif圖片等等均無法使用。如果想要動態(tài)的去更新infowindow內(nèi)容介粘,請務(wù)必仔細(xì)看看此接口的更新機(jī)制殖氏。
更新機(jī)制
設(shè)置此接口返回值之后,會定期(默認(rèn)周期無窮大)調(diào)用一個
getInfoWindow(Marker)
并將View轉(zhuǎn)換為圖片姻采。由于將View轉(zhuǎn)成圖片會比較耗時雅采,不能一直調(diào)用,而設(shè)置時間間隔可以減少一定的耗時慨亲。
調(diào)用
Marker.showInfoWindow()
也可以觸發(fā)調(diào)用AMap.InfoWindowAdapter.getInfoWindow(Marker)
并將View轉(zhuǎn)換為圖片婚瓜。
類型 | 方法 | 說明 |
---|---|---|
long | getInfoWindowUpdateTime() | 自定義整個信息窗口屬性間隔時間。單位為 ms |
方法說明
- 如果返回值 小于或等于 0刑棵,則認(rèn)為是無窮大巴刻。
- 如果返回值 (0,100] , 則認(rèn)為是100(如果頻繁將View轉(zhuǎn)成圖片,內(nèi)存抖動會很嚴(yán)重蛉签,建議這個值不要太低)胡陪。
- 如果這個想實現(xiàn)更小的時間間隔或者不想受這個接口約束,可以保持返回默認(rèn)值正蛙,并自行設(shè)置計時器督弓。
示例
界面布局
- 布局文件
<?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="適配器(內(nèi)容)"
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<>();
// 選中的狀態(tài)
String selectedFlag = SIMPlE_MODE;
// 氣泡圖標(biāo)
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() {
// 設(shè)置marker的信息窗口點擊事件監(jiān)聽接口。
map.setOnInfoWindowClickListener(new AMap.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
// 隱藏Marker覆蓋物的信息窗口乒验。
marker.hideInfoWindow();
}
});
}
創(chuàng)建與移除覆蓋物
public void addMarkers() {
// 構(gòu)造大量坐標(biāo)數(shù)據(jù)
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));
// 創(chuàng)建OverlayOptions的集合
ArrayList<MarkerOptions> optionsList = new ArrayList<>();
for (int i = 0; i < points.size(); ++i) {
// 創(chuàng)建OverlayOptions屬性
MarkerOptions option = new MarkerOptions()
.position(points.get(i))
.icon(bitmaps.get(i))
.title("標(biāo)題" + (i + 1))
.snippet("詳細(xì)信息" + (i + 1));
// 將OverlayOptions添加到list
optionsList.add(option);
}
boolean moveToCenter = true;
// 在地圖上添一組圖片標(biāo)記(marker)對象愚隧,
// 并設(shè)置是否改變地圖狀態(tài)以至于所有的marker對象都在當(dāng)前地圖可視區(qū)域范圍內(nèi)顯示。
ArrayList<Marker> newOverlays = map.addMarkers(optionsList, moveToCenter);
overlays.addAll(newOverlays);
}
public void removeOverlay() {
// 從地圖上刪除所有的覆蓋物(marker锻全,circle狂塘,polyline 等對象),
// 但myLocationOverlay(內(nèi)置定位覆蓋物)除外鳄厌。
// boolean isKeepMyLocationOverlay = true;
// map.clear(isKeepMyLocationOverlay);
for (BaseOverlay overlay : overlays) {
if (overlay instanceof Marker) {
Marker marker = (Marker) overlay;
marker.hideInfoWindow();
}
}
overlays.clear();
}
設(shè)置屬性
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方法(設(shè)置
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類部分代碼
控件響應(yīng)事件
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);
}
運行效果圖
簡單 | 適配器(窗口) | 適配器(內(nèi)容) |
---|---|---|
2-效果圖1.png
|
2-效果圖2.png
|
2-效果圖3.png
|
默認(rèn)樣式 | 自定義樣式1 | 自定義樣式2 |
- 自定義樣式1:
background
+image
+title
+snippet
- 自定義樣式2:
image
+title
+snippet