📖第4章 Android高德地圖繪制標記點Marker
- ?繪制默認 Marker
- ?繪制多個Marker
- ?繪制自定義 Marker
- ?Marker點擊事件
- ?Marker動畫效果
- ?Marker拖拽事件
- ?繪制默認 Infowindow
- 🚩隱藏InfoWindow 彈框
- ?繪制自定義 InfoWindow
- 🚩實現 InfoWindow 樣式和內容
- 🚩可觸發的 InfoWindow 事件
- 🚩自定義復雜的 InfoWindow
?繪制默認 Marker
效果如下圖:
通過aMap.addMarker()
來添加標記點marker
,而經緯度等信息需要通過MarkerOptions
來設置,示例代碼如下:
//marker標記物
LatLng latLng = new LatLng(31.042119,121.410428);final Marker marker = aMap.addMarker(new MarkerOptions().position(latLng).title("測試地點").snippet("這里是測試內容"));
Marker 常用屬性
名稱 | 說明 |
---|---|
position | 在地圖上標記位置的經緯度值。必填參數 |
title | 點標記的標題 |
snippet | 點標記的內容 |
draggable | 點標記是否可拖拽 |
visible | 點標記是否可見 |
anchor | 點標記的錨點 |
alpha | 點的透明度 |
anchor
錨點可以精確控制標記圖標相對于標記點(經緯度)的位置,以滿足不同場景下的需求。比如您可能希望將錨點設置為標記圖標的其他部分,例如頂部中心或左側中心。默認錨點位置是底部中心
anchor(0.0f, 0.5f); // 左側中心
anchor(1.0f, 0.5f); // 右部中心
anchor(1.0f, 1.0f); // 底部右側
anchor(0.5f, 0.0f); // 頂部中心
alpha
透明度是用來表示對象的可見度或不透明度的屬性。應用場景:
- 標記動畫:實現標記的淡入淡出效果,以改善用戶體驗
// 創建標記并設置透明度為0.5
MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(latitude, longitude)).icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_icon)).alpha(0.5f); // 設置透明度為0.5
Marker marker = aMap.addMarker(markerOptions);// 在動畫中逐漸將透明度變為1.0
ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(marker, "alpha", 0.5f, 1.0f);
alphaAnimator.setDuration(1000);
alphaAnimator.start();
- 突出顯示標記: 在一組標記中突出顯示特定的標記,可以使目標標記更加顯眼。
// 創建多個標記
MarkerOptions targetMarkerOptions = new MarkerOptions().position(new LatLng(targetLatitude, targetLongitude)).icon(BitmapDescriptorFactory.fromResource(R.drawable.target_marker_icon)).alpha(1.0f); // 目標標記的透明度為1.0
Marker targetMarker = aMap.addMarker(targetMarkerOptions);MarkerOptions otherMarkerOptions = new MarkerOptions().position(new LatLng(otherLatitude, otherLongitude)).icon(BitmapDescriptorFactory.fromResource(R.drawable.other_marker_icon)).alpha(0.5f); // 其他標記的透明度為0.5
Marker otherMarker = aMap.addMarker(otherMarkerOptions);
- 動態顯示與隱藏:將透明度設置為0.0時,標記將完全不可見,而設置為1.0時,則完全可見。
// 創建標記并設置透明度為0.0,標記開始時不可見
MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(latitude, longitude)).icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_icon)).alpha(0.0f); // 設置透明度為0.0
Marker marker = aMap.addMarker(markerOptions);// 在動畫中逐漸將透明度變為1.0,標記逐漸變得可見
ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(marker, "alpha", 0.0f, 1.0f);
alphaAnimator.setDuration(1000);
alphaAnimator.start();
?繪制多個Marker
效果如下圖:
不管是創建默認的Marker
還是自定義的Marker
都一樣,都是通過MarkerOptions
設置Marker
的信息,再通過aMap.addMarker(markerOption)
在地圖上添加。
示例代碼如下:
//樣本數據List<LatLng> positon = new ArrayList<>();positon.add(new LatLng(31.041742,121.411517));positon.add(new LatLng(31.041370,121.411699));positon.add(new LatLng(31.041563,121.412198));//繪制自定義markerMarkerOptions options = new MarkerOptions();for (int i = 0; i < positon.size(); i++) {options.position(positon.get(i));options.title("測試"+i);options.snippet("內容"+i);aMap.addMarker(options);}
?繪制自定義 Marker
效果如下圖:
繪制自定義 Marker
的自定義icon
圖標是通過BitmapDescriptorFactory
來處理,它能將圖標資源文件轉換成位圖(Bitmap)對象,以便在地圖上使用。示例代碼如下:
//繪制自定義markerLatLng latLng2 = new LatLng(31.041991,121.409628);MarkerOptions markerOption = new MarkerOptions();markerOption.position(latLng2);markerOption.title("測試2").snippet("我是自定義marker");markerOption.draggable(true);//設置Marker可拖動markerOption.icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.icon_marker_orange)));markerOption.setFlat(false);//設置marker平貼地圖效果aMap.addMarker(markerOption);
至于icon
圖標可以在阿里巴巴矢量圖標庫里面下載png
格式的圖片即可。
🚩擴展:在自定義的marker中繪制文字等其他信息
應用場景:需要在地圖上看見該標記點中的數據信息,可以是姓名簡稱,數字等信息。 效果如下圖:
一共兩個步驟:
- 繪制帶文本的圖片,格式為
BitmapDescriptor
類型 - 在地圖上添加標記點
//繪制自定義帶文字的markerBitmapDescriptor withDataIcon = drawIcon(R.drawable.icon_marker_orange, "李", Color.WHITE);LatLng latLng2 = new LatLng(31.041991,121.409628);MarkerOptions markerOption = new MarkerOptions();markerOption.position(latLng2);markerOption.title("測試").snippet("我是攜帶數據的marker");markerOption.draggable(true);//設置Marker可拖動markerOption.icon(withDataIcon);markerOption.setFlat(false);//設置marker平貼地圖效果aMap.addMarker(markerOption);
drawIcon
方法如下:
/*** 繪制帶文字的marker* @param markerStyle 圖片資源* @param text 文字* @param textColor 文字顏色* @return*/private BitmapDescriptor drawIcon(int markerStyle, String text, int textColor) {Bitmap bitmap = null;BitmapDescriptor icon = null;try {bitmap = BitmapFactory.decodeResource(getResources(), markerStyle);//圖片轉bitmap位圖int markerStyleWidth = bitmap.getWidth();//獲取bitmap位圖的寬int markerStyleHeight = bitmap.getHeight();//獲取bitmap位圖的高Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);//復制Bitmap對象Canvas canvas = new Canvas(mutableBitmap); //創建Canvas對象Paint paint = new Paint();//創建Paint對象,用于定義繪制文本的樣式paint.setColor(textColor);//設置文字顏色paint.setTextSize(getResources().getDimensionPixelSize(R.dimen.text_size));//設置文字大小//獲取文本邊界Rect textBounds = new Rect();paint.getTextBounds(text, 0, text.length(), textBounds);int textWidth = textBounds.width();//文本寬度int textHeight = textBounds.height();//文本高度//計算文字在圖片上的坐標,使其在圖片居中位置float x = (markerStyleWidth - textWidth) / 2.0f;//x表示繪制文本起始點的橫坐標,從左開始float y = (markerStyleHeight + textHeight) / 2.0f - 20;//x表示繪制文本起始點的縱坐標,從頂部開始canvas.drawText(text, x, y, paint); //使用Canvas繪制文本icon = BitmapDescriptorFactory.fromBitmap(mutableBitmap);// 最后將Bitmap 轉換為 BitmapDescriptorreturn icon;} finally {if (bitmap != null){bitmap.recycle();}if (icon != null){icon.recycle();}}}
注意:
Bitmap
和BitmapDescriptor
是一個占用內存的對象,需要及時回收以防止內存泄漏。雖然Java有垃圾回收機制,但它是在適當的時機,例如在內存緊張或空閑時,掃描不再被引用的對象并將其釋放,在某些情況下,手動釋放資源仍然是一個良好的習慣。R.dimen.text_size
是在values文件下的dimens.xml
資源文件中定義的,如果你沒有則創建一個并添加下面代碼。
<resources><dimen name="text_size">14sp</dimen>
</resources>
?Marker點擊事件
點擊 Marker 時會回調AMap.OnMarkerClickListener
,監聽器的實現示例如下:
//設置marker點擊事件aMap.setOnMarkerClickListener(this);
重寫onMarkerClick
方法
/*** marker 點擊監聽事件* 返回 true 則表示接口已響應事件,否則返回false* @param marker* @return*/@Overridepublic boolean onMarkerClick(Marker marker) {return false;}
?Marker動畫效果
將動畫效果放在點擊marker時可以更好看到變化,自地圖 SDK V4.0.0 版本起,SDK 提供了給 Marker 設置動畫的方法,具體實現方法如下:
/*** marker 點擊監聽事件* 返回 true 則表示接口已響應事件,否則返回false* @param marker* @return*/@Overridepublic boolean onMarkerClick(Marker marker) {//180度旋轉動畫Animation animation = new RotateAnimation(marker.getRotateAngle(),marker.getRotateAngle()+180,0,0,0);long duration = 1000L;animation.setDuration(duration);animation.setInterpolator(new LinearInterpolator());marker.setAnimation(animation);marker.startAnimation();return false;}
?Marker拖拽事件
拖拽 Marker
時會回調AMap.OnMarkerDragListener
,監聽器的實現示例如下:
//設置marker拖拽監聽事件aMap.setOnMarkerDragListener(this);
/*** 當marker開始被拖動時回調此方法, 這個marker的位置可以通過getPosition()方法返回。* @param marker*/@Overridepublic void onMarkerDragStart(Marker marker) {Log.d("MainActivity", "Start: "+marker.getPosition());}/*** 在marker拖動過程中回調此方法, 這個marker的位置可以通過getPosition()方法返回。* @param marker*/@Overridepublic void onMarkerDrag(Marker marker) {Log.d("MainActivity", "Drag: "+marker.getPosition());}/*** 在marker拖動完成后回調此方法, 這個marker的位置可以通過getPosition()方法返回。* @param marker*/@Overridepublic void onMarkerDragEnd(Marker marker) {Log.d("MainActivity", "End: "+marker.getPosition());}
?繪制默認 Infowindow
默認 Infowindow是不用創建的,當我們創建marker時自帶有的,SDK
為用戶提供了默認的 InfoWindow 樣式,只顯示 Marker 對象的兩個屬性,一個是 title
和另一個 snippet
。
調用 Marker 類的 showInfoWindow()
和 hideInfoWindow()
方法可以控制顯示和隱藏。
當改變 Marker 的 title 和 snippet 屬性時,再次調用 showInfoWindow()
,可以更新 InfoWindow 顯示內容。
🚩隱藏InfoWindow 彈框
在什么時候去隱藏InfoWindow 彈框,一般情況可以在點擊map地圖的其他地方關閉它,實現方式如下:
- 首先設置
marker
和map
點擊監聽事件
//設置marker點擊事件
aMap.setOnMarkerClickListener(this);
//地圖點擊監聽事件
aMap.setOnMapClickListener(this);
- 然后在
marker
點擊事件中記錄當前點擊的marker
和Infowindow
是否彈框的布爾值
private Marker current;//記錄當前點擊的marker
private boolean isMarkerClicked = false;//判斷是否Infowindow彈框
@Overridepublic boolean onMarkerClick(Marker marker) {current = marker;isMarkerClicked = true;return false;}
- 最后在
onMapClick
地圖點擊事件中隱藏Infowindow
彈框
/*** map地圖點擊監聽事件*/@Overridepublic void onMapClick(LatLng latLng) {if (!isMarkerClicked) {if (current != null && current.isInfoWindowShown()) {current.hideInfoWindow();//隱藏當前Infowindow彈框}}isMarkerClicked = false; // 重置標記狀態}
?繪制自定義 InfoWindow
實現 InfoWindowAdapter
接口,其中有兩個方法需要實現,依次來看一下:
public interface InfoWindowAdapter {View getInfoWindow(Marker marker);View getInfoContents(Marker marker);
}
View getInfoWindow(Marker marker)
- 當實現此方法并返回有效值時(返回值不為空,則視為有效),SDK 將不會使用默認的樣式,而采用此方法返回的樣式(即 View)。默認會將Marker 的 title 和 snippet 顯示到 InfoWindow 中。
- 如果此時修改了 Marker 的 title 或者 snippet 內容,再次調用類 Marker 的 showInfoWindow() 方法,InfoWindow 內容不會更新。
- 自定義 InfoWindow 之后所有的內容更新都需要用戶自己完成。
- 當調用 Marker 類的 showInfoWindow() 方法時,SDK 會調用 getInfoWindow(Marker marker) 方法和 getInfoContents(Marker marker) 方法(之后會提到),在這些方法中更新 InfoWindow 的內容即可。
注意:如果此方法返回的 View 沒有設置 InfoWindow 背景圖,SDK 會默認添加一個背景圖。
View getInfoContents(Marker marker)
此方法和 getInfoWindow(Marker marker) 方法的實質是一樣的,唯一的區別是:
- 此方法不能修改整個 InfoWindow 的背景和邊框,無論自定義的樣式是什么樣,SDK 都會在最外層添加一個默認的邊框。
簡而言之,getInfoContents(Marker marker)
只允許你自定義 InfoWindow
的內容,而不能改變整個窗口的外觀和邊框。SDK 會在你自定義的內容外面添加一個默認的邊框。這可能是為了確保 InfoWindow
在地圖上有一致的外觀,以保持用戶體驗的統一性。如果你希望完全自定義整個 InfoWindow
的外觀,包括邊框,那么應該使用 getInfoWindow(Marker marker)
方法。
🚩實現 InfoWindow 樣式和內容
實現 InfoWindow
樣式和內容的步驟如下:
- 設置InfoWindow適配器
- 在
getInfoWindow
返回一個自定義View
組件 =R.layout.custom_info_window
。
implements AMap.InfoWindowAdapter//實現接口aMap.setInfoWindowAdapter(this);//設置InfoWindow適配器
實現getInfoWindow和getInfoContents方法
View infoWindow = null;@Overridepublic View getInfoWindow(Marker marker) {if (infoWindow == null) {infoWindow = LayoutInflater.from(this).inflate(R.layout.custom_info_window, null);}render(marker, infoWindow);return infoWindow;}@Overridepublic View getInfoContents(Marker marker) {return null;}public void render(Marker marker, View view) {//如果想修改自定義Infow中內容,請通過view找到它并修改String title = marker.getTitle();TextView titleUi = view.findViewById(R.id.title);titleUi.setText(title);String snippet = marker.getSnippet();TextView snippetUi = view.findViewById(R.id.snippet);snippetUi.setText(snippet);}
R.layout.custom_info_window
組件:
<?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:gravity="center"android:orientation="vertical"><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="17sp"android:textColor="#72db0f"android:text="標題"/><TextViewandroid:id="@+id/snippet"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="17sp"android:textColor="#db0f00"android:text="詳細內容"/>
</LinearLayout>
🚩可觸發的 InfoWindow 事件
點擊 InfoWindow
時會回調 AMap.OnInfoWindowClickListener
,監聽器的實現示例如下:
OnInfoWindowClickListener listener = new OnInfoWindowClickListener() {@Overridepublic void onInfoWindowClick(Marker marker) {marker.setTitle("infowindow clicked");}
};//綁定信息窗點擊事件
aMap.setOnInfoWindowClickListener(listener);
簡單點說就是可以自定義點擊 InfoWindow
后的行為,例如改變標題、展示更多信息或觸發其他操作。它有哪些使用場景和用途:
- 刷新 InfoWindow 內容: 在點擊事件中,您可以修改
InfoWindow
的內容,使其動態刷新,顯示最新的信息。這對于實時更新的信息非常有用。 - **交互式地圖體驗:**例如點擊時顯示更多的內容、點擊后關閉
InfoWindow
彈框,marker.hideInfoWindow()
即可。 - **與標記點關聯的操作:**例如,在地圖上顯示商家的標記點,點擊
InfoWindow
可以跳轉到商家詳情頁面。
等等還要其他的,這里就舉例這幾種。
🚩自定義復雜的 InfoWindow
根據需求,你可能需求對標記點進行各種操作,如下圖:
其實繪制自定義 InfoWindow
本質上就是兩步,先設置InfoWindow
適配器,其次在view組件中自定義要顯示的樣式或按鈕。
在這種自定義可能會遇到居中顯示問題,如何點擊標點時讓其在屏幕中心,或者在屏幕中心下方一點點使其能都剛好在屏幕中心完全顯示。只需在onMarkerClick
點擊事件中實現如下邏輯:
- 將
marker
位置坐標下移一小段距離顯示 - 注意實現
onMarkerClick
前需要aMap.setOnMarkerClickListener(this);
設置
@Overridepublic boolean onMarkerClick(Marker marker) {// 當前點擊的marker位置坐標LatLng markerLatLng = marker.getPosition();// 計算新坐標(下移一小段距離)double offsetLat = 0.003; LatLng newCenterLatLng = new LatLng(markerLatLng.latitude + offsetLat, markerLatLng.longitude);// 創建一個新的 CameraPosition,設置新的方向為 0(北方向)CameraPosition newPosition = new CameraPosition.Builder().target(newCenterLatLng).bearing(0) // 將方向設置為 0(北方向).zoom(15) // 設置縮放級別.build();// 移動地圖中心點aMap.animateCamera(CameraUpdateFactory.newCameraPosition(newPosition));return true; // 返回 true 表示消費了點擊事件,不再傳遞給其他監聽器}
?第1,2章 Android搭建3D高德地圖詳細教
?第 3 章 初始化定位并顯示小藍點
?第 4 章Android高德地圖繪制標記點Marker
?第 5 章Android高德地圖控件交互詳細介紹