高德地圖SDK Android版開發(fā) 10 InfoWindow

前言

前文介紹高德地圖添加Marker覆蓋物的使用方法氮块,Marker結(jié)合InfoWindow可展示更詳盡的信息宏榕。本文將介紹以下內(nèi)容:

  1. 使用SDK默認(rèn)樣式顯示InfoWindow的方法棠赛;
  2. 自定義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)改變 Markertitlesnippet 屬性時氢哮,再次調(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 接口

image.png

用途:

用來實現(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è)置計時器督弓。

示例

界面布局

1-布局.png
  • 布局文件
<?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荞胡,WindowModeAdapterContentModeAdapter為自定義兩個適配器。代碼和布局見附錄了嚎。

自定義樣式

  • 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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泪漂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子歪泳,更是在濱河造成了極大的恐慌萝勤,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呐伞,死亡現(xiàn)場離奇詭異敌卓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)伶氢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門趟径,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘪吏,“玉大人,你說我怎么就攤上這事蜗巧≌泼撸” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵幕屹,是天一觀的道長扇救。 經(jīng)常有香客問我,道長香嗓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任装畅,我火速辦了婚禮靠娱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘掠兄。我一直安慰自己像云,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布蚂夕。 她就那樣靜靜地躺著迅诬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪婿牍。 梳的紋絲不亂的頭發(fā)上侈贷,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音等脂,去河邊找鬼俏蛮。 笑死,一個胖子當(dāng)著我的面吹牛上遥,可吹牛的內(nèi)容都是我干的搏屑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粉楚,長吁一口氣:“原來是場噩夢啊……” “哼辣恋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起模软,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤伟骨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后撵摆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體底靠,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年特铝,在試婚紗的時候發(fā)現(xiàn)自己被綠了暑中。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壹瘟。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鳄逾,靈堂內(nèi)的尸體忽然破棺而出稻轨,到底是詐尸還是另有隱情,我是刑警寧澤雕凹,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布殴俱,位于F島的核電站,受9級特大地震影響枚抵,放射性物質(zhì)發(fā)生泄漏线欲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一汽摹、第九天 我趴在偏房一處隱蔽的房頂上張望李丰。 院中可真熱鬧,春花似錦逼泣、人聲如沸趴泌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗜憔。三九已至,卻和暖如春氏仗,著一層夾襖步出監(jiān)牢的瞬間吉捶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工廓鞠, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留帚稠,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓床佳,卻偏偏與公主長得像滋早,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子砌们,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容