Sfoglia il codice sorgente

[new]轨迹时间轴item增加停留点popup显示功能

zk 6 mesi fa
parent
commit
a64544ef0a
19 ha cambiato i file con 417 aggiunte e 69 eliminazioni
  1. 1 0
      lib/data/consts/constants.dart
  2. 12 3
      lib/module/track/track_controller.dart
  3. 44 36
      lib/module/track/track_day_detail/track_daily_item.dart
  4. 19 1
      lib/module/track/track_day_detail/track_day_detail_controller.dart
  5. 2 1
      lib/module/track/track_day_detail/track_day_detail_view.dart
  6. 1 0
      plugins/map/lib/flutter_map.dart
  7. 2 0
      plugins/map/lib/src/consts/map_constants.dart
  8. 20 0
      plugins/map/lib/src/core/map_controller.dart
  9. 7 0
      plugins/map/lib/src/entity/marker.dart
  10. 27 0
      plugins/map/lib/src/entity/popup.dart
  11. 3 0
      plugins/map/lib/src/interface/map_fun_interface.dart
  12. 90 1
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/MapController.java
  13. 11 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/bean/MakerInfo.java
  14. 56 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/bean/Popup.java
  15. 2 1
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/contants/Constants.java
  16. 48 26
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/marker/MarkersController.java
  17. BIN
      plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/bg_track_popup_bubble.9.png
  18. BIN
      plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_track_stay.webp
  19. 72 0
      plugins/map_amap_android/android/src/main/res/layout/item_track_selected_marker.xml

+ 1 - 0
lib/data/consts/constants.dart

@@ -39,6 +39,7 @@ class Constants {
   static const String mineLocationId = '';
   static const String traceStartId = '-100';
   static const String traceEndId = '-200';
+  static const String traceSelectId = '-300';
 
   static const double blurredX = 4.2;
   static const double blurredY = 4.2;

+ 12 - 3
lib/module/track/track_controller.dart

@@ -32,6 +32,12 @@ class TrackController extends BaseController
 
   final Rxn<TrackDays> currentTrackDay = Rxn();
 
+  final mapPadding =
+      MapPadding(left: 50.w, top: 100.w, right: 50.w, bottom: Get.height / 2);
+
+  final selectPadding =
+  MapPadding(left: 80.w, top: 150.w, right: 80.w, bottom: Get.height / 2);
+
   final TrackRepository trackRepository;
   final FriendsRepository friendsRepository;
   final AccountRepository accountRepository;
@@ -96,9 +102,7 @@ class TrackController extends BaseController
     mapController.clear();
     //画折线
     if (points.length > 1) {
-      mapController.addPolyline(points,
-          mapPadding: MapPadding(
-              left: 50.w, top: 100.w, right: 50.w, bottom: Get.height / 2));
+      mapController.addPolyline(points, mapPadding: mapPadding);
     }
     //画标记点
     if (markers.isNotEmpty) {
@@ -106,6 +110,11 @@ class TrackController extends BaseController
     }
   }
 
+  void showSelectMarker(List<LatLng> points, Marker selectMarker) {
+    mapController.updateOrAddMarker(selectMarker);
+    mapController.moveToSuitableLocation(points, mapPadding: selectPadding);
+  }
+
   void back() {
     Get.back();
   }

+ 44 - 36
lib/module/track/track_day_detail/track_daily_item.dart

@@ -14,7 +14,10 @@ import 'package:location/utils/date_util.dart';
 
 import '../track_status.dart';
 
-Widget buildTrackDailyItem(TrackDailyBean bean, bool isEnd) {
+typedef TrackItemClick = void Function(TrackDailyBean bean);
+
+Widget buildTrackDailyItem(TrackDailyBean bean, bool isEnd,
+    {TrackItemClick? onItemClick}) {
   return Container(
     padding: EdgeInsets.symmetric(horizontal: 12.w),
     margin: EdgeInsets.only(bottom: 8.w),
@@ -25,7 +28,7 @@ Widget buildTrackDailyItem(TrackDailyBean bean, bool isEnd) {
           if (bean.status == TrackStatus.moving) {
             return _buildMovingTrackDailyItem(bean);
           } else if (bean.status == TrackStatus.stay) {
-            return buildStayTrackDailyItem(bean);
+            return buildStayTrackDailyItem(bean, onItemClick: onItemClick);
           } else if (bean.status == TrackStatus.error) {
             return buildErrorTrackDailyItem(bean);
           } else {
@@ -97,7 +100,7 @@ Widget _buildMovingTrackDailyItem(TrackDailyBean bean) {
 }
 
 Widget buildStayTrackDailyItem(TrackDailyBean bean,
-    {EdgeInsetsGeometry? contentPadding}) {
+    {EdgeInsetsGeometry? contentPadding, TrackItemClick? onItemClick}) {
   return Column(
     crossAxisAlignment: CrossAxisAlignment.start,
     children: [
@@ -123,40 +126,45 @@ Widget buildStayTrackDailyItem(TrackDailyBean bean,
               ],
             ),
             Expanded(
-                child: Container(
-              padding: EdgeInsets.all(10.w),
-              margin: contentPadding ?? EdgeInsets.only(top: 20.w),
-              decoration: BoxDecoration(
-                  borderRadius: BorderRadius.circular(8.r),
-                  gradient: LinearGradient(colors: [
-                    '#F8F5FF'.color,
-                    ColorName.transparent,
-                  ])),
-              child: ConstrainedBox(
-                constraints: BoxConstraints(minHeight: 60.w),
-                child: Column(
-                  crossAxisAlignment: CrossAxisAlignment.start,
-                  children: [
-                    Expanded(
-                      child: Text(
-                        bean.addr ?? '',
-                        style: TextStyle(
-                            fontSize: 12.sp,
-                            color: '#333333'.color,
-                            fontWeight: FontWeight.bold),
+                child: GestureDetector(
+              onTap: () {
+                onItemClick?.call(bean);
+              },
+              child: Container(
+                padding: EdgeInsets.all(10.w),
+                margin: contentPadding ?? EdgeInsets.only(top: 20.w),
+                decoration: BoxDecoration(
+                    borderRadius: BorderRadius.circular(8.r),
+                    gradient: LinearGradient(colors: [
+                      '#F8F5FF'.color,
+                      ColorName.transparent,
+                    ])),
+                child: ConstrainedBox(
+                  constraints: BoxConstraints(minHeight: 60.w),
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    children: [
+                      Expanded(
+                        child: Text(
+                          bean.addr ?? '',
+                          style: TextStyle(
+                              fontSize: 12.sp,
+                              color: '#333333'.color,
+                              fontWeight: FontWeight.bold),
+                        ),
                       ),
-                    ),
-                    SizedBox(height: 11.w),
-                    Row(
-                      children: [
-                        _buildStayDesc(bean.duration),
-                        SizedBox(width: 18.w),
-                        _buildLockDesc(bean.highUnlock, bean.totalUnlock),
-                        SizedBox(width: 18.w),
-                        Expanded(child: _buildNetDesc(bean.network))
-                      ],
-                    )
-                  ],
+                      SizedBox(height: 11.w),
+                      Row(
+                        children: [
+                          _buildStayDesc(bean.duration),
+                          SizedBox(width: 18.w),
+                          _buildLockDesc(bean.highUnlock, bean.totalUnlock),
+                          SizedBox(width: 18.w),
+                          Expanded(child: _buildNetDesc(bean.network))
+                        ],
+                      )
+                    ],
+                  ),
                 ),
               ),
             ))

+ 19 - 1
lib/module/track/track_day_detail/track_day_detail_controller.dart

@@ -7,6 +7,7 @@ import 'package:get/get.dart';
 import 'package:get/get_core/src/get_main.dart';
 import 'package:location/base/base_controller.dart';
 import 'package:location/data/bean/track_daily_bean.dart';
+import 'package:location/data/consts/constants.dart';
 import 'package:location/data/repositories/track_repository.dart';
 import 'package:location/dialog/loading_dialog.dart';
 import 'package:location/handler/error_handler.dart';
@@ -16,7 +17,6 @@ import 'package:location/module/track/track_util.dart';
 import 'package:location/resource/string.gen.dart';
 import 'package:location/sdk/wechat/wechat_share_util.dart';
 import 'package:location/utils/async_util.dart';
-import 'package:location/utils/atmob_log.dart';
 import 'package:location/utils/common_expand.dart';
 import 'package:location/utils/pair.dart';
 import 'package:location/utils/toast_util.dart';
@@ -377,6 +377,24 @@ class TrackDayDetailController extends BaseController {
     WechatShareUtil.shareWidgetToWeChat(shareGlobalKey);
   }
 
+  void onHistoryTrackItemClick(TrackDailyBean bean) {
+    if (bean.status == TrackStatus.stay) {
+      //停留状态
+      Marker selectMarker = Marker(
+        id: Constants.traceSelectId,
+        markerName: '',
+        latitude: bean.lat,
+        longitude: bean.lng,
+        markerType: MarkerType.traceSelectPoint,
+        popup: Popup(
+          title: bean.addr ?? '',
+          desc: TrackUtil.formatDurationFromMillis(bean.duration),
+        ),
+      );
+      trackController.showSelectMarker(points, selectMarker);
+    }
+  }
+
   @override
   void onClose() {
     super.onClose();

+ 2 - 1
lib/module/track/track_day_detail/track_day_detail_view.dart

@@ -415,6 +415,7 @@ class TrackDayDetailView extends BaseView<TrackDayDetailController> {
 
   Widget buildHistoryTrackItem(BuildContext context, int index) {
     return buildTrackDailyItem(controller.trackDailyList[index],
-        index == controller.trackDailyList.length - 1);
+        index == controller.trackDailyList.length - 1,
+        onItemClick: controller.onHistoryTrackItemClick);
   }
 }

+ 1 - 0
plugins/map/lib/flutter_map.dart

@@ -18,6 +18,7 @@ export 'package:flutter_map/src/entity/map_location.dart';
 export 'package:flutter_map/src/entity/trace_location.dart';
 export 'package:flutter_map/src/entity/lat_lng.dart';
 export 'package:flutter_map/src/entity/map_padding.dart';
+export 'package:flutter_map/src/entity/popup.dart';
 
 //接口
 export 'package:flutter_map/src/interface/map_sdk_interface.dart';

+ 2 - 0
plugins/map/lib/src/consts/map_constants.dart

@@ -24,6 +24,8 @@ class MapConstants {
   static const String methodMapMoveCamera = "map#moveCamera";
   static const String methodMapAnimateCamera = "map#animateCamera";
   static const String methodMapClear = "map#clear";
+  static const String methodMoveToSuitableLocation =
+      "map#moveToSuitableLocation";
 
   //标记物相关方法名称
   static const String methodUpdateOrAddMarkers = "marker#updateOrAddMarkers";

+ 20 - 0
plugins/map/lib/src/core/map_controller.dart

@@ -150,6 +150,26 @@ class MapController
     }
   }
 
+  @override
+  void moveToSuitableLocation(List<LatLng> points, {MapPadding? mapPadding}) {
+    if (_isDisposed || points.isEmpty) return;
+    Map<String, dynamic> map = {};
+    map['points'] = _encodeJson(points);
+    if (mapPadding != null) {
+      map['mapPadding'] = _encodeJson(mapPadding);
+    }
+    final params = {
+      'method': MapConstants.methodMoveToSuitableLocation,
+      'args': map
+    };
+    debugPrint("moveToSuitableLocation...params==>$params");
+    if (_channel != null) {
+      _executeMethod(params);
+    } else {
+      _pendingOperations.add(params);
+    }
+  }
+
   /// iOS 需要一个Map 而不是json string
   dynamic _encodeJson(dynamic object) {
     // iOS平台直接返回对象或转换为JSON对象

+ 7 - 0
plugins/map/lib/src/entity/marker.dart

@@ -1,3 +1,5 @@
+import 'package:flutter_map/src/entity/popup.dart';
+
 import '../consts/marker_type.dart';
 import "codable.dart";  
 
@@ -16,6 +18,8 @@ class Marker implements Codable {
 
   String? customAvatarUrl;
 
+  Popup? popup;
+
   Marker({
     required this.id,
     required this.markerName,
@@ -24,6 +28,7 @@ class Marker implements Codable {
     required this.markerType,
     this.isSelected = false,
     this.customAvatarUrl,
+    this.popup,
   });
 
   @override
@@ -36,6 +41,7 @@ class Marker implements Codable {
       'markerType': markerType.value,
       'isSelected': isSelected,
       'customAvatarUrl': customAvatarUrl,
+      'popup': popup?.toJson(),
     };
   }
 
@@ -48,6 +54,7 @@ class Marker implements Codable {
       markerType: MarkerType.fromValue(map['markerType'] as int),
       isSelected: map['isSelected'],
       customAvatarUrl: map['customAvatarUrl'] as String?,
+      popup: map['popup'] != null ? Popup.fromJson(map['popup']) : null,
     );
   }
 }

+ 27 - 0
plugins/map/lib/src/entity/popup.dart

@@ -0,0 +1,27 @@
+import 'codable.dart';
+
+class Popup implements Codable {
+
+  String title;
+  String desc;
+
+  Popup({
+    required this.title,
+    required this.desc,
+  });
+
+  @override
+  Map<String, dynamic> toJson() {
+    return {
+      'title': title,
+      'desc': desc,
+    };
+  }
+
+  factory Popup.fromJson(Map<String, dynamic> map) {
+    return Popup(
+      title: map['title'] as String,
+      desc: map['desc'] as String,
+    );
+  }
+}

+ 3 - 0
plugins/map/lib/src/interface/map_fun_interface.dart

@@ -9,4 +9,7 @@ abstract class MapFunInterface {
 
   //清除地图所有标记
   void clear();
+
+  //移动至多个点的位置,并提供设置padding距离
+  moveToSuitableLocation(List<LatLng> points, {MapPadding? mapPadding});
 }

+ 90 - 1
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/MapController.java

@@ -5,12 +5,21 @@ import android.content.Context;
 import androidx.annotation.NonNull;
 
 import com.amap.api.maps.AMap;
+import com.amap.api.maps.CameraUpdate;
 import com.amap.api.maps.CameraUpdateFactory;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.LatLngBounds;
+import com.atmob.map_amap_android.bean.MapPadding;
 import com.atmob.map_amap_android.contants.Constants;
 import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
+import com.atmob.map_amap_android.util.GsonUtil;
 import com.atmob.map_amap_android.util.LogUtil;
 import com.atmob.map_amap_android.util.ParamUtil;
+import com.atmob.map_amap_android.util.SizeUtil;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
 
+import java.util.List;
 import java.util.Map;
 
 import io.flutter.plugin.common.MethodCall;
@@ -26,10 +35,25 @@ public class MapController implements MyMethodCallHandler {
 
     private final MethodChannel methodChannel;
 
+    int nowMapTypeIndex = 0;
+
+    private final Gson gson;
+
+    private final int[] mapType = {
+            AMap.MAP_TYPE_NORMAL,
+            AMap.MAP_TYPE_SATELLITE,
+            AMap.MAP_TYPE_NIGHT,
+            AMap.MAP_TYPE_NAVI,
+            AMap.MAP_TYPE_BUS,
+            AMap.MAP_TYPE_NAVI_NIGHT,
+    };
+
     public MapController(Context context, MethodChannel methodChannel, AMap map) {
         this.context = context;
         this.methodChannel = methodChannel;
         this.map = map;
+        map.setMapType(mapType[nowMapTypeIndex]);
+        gson = GsonUtil.getInstance();
     }
 
 
@@ -42,12 +66,77 @@ public class MapController implements MyMethodCallHandler {
             case Constants.METHOD_ANIMATE_CAMERA:
                 animateCamera(call, result);
                 break;
-                case Constants.METHOD_MAP_CLEAR:
+            case Constants.METHOD_MAP_CLEAR:
                 clearMap(result);
                 break;
+            case Constants.METHOD_MAP_MOVE_TO_SUITABLE_LOCATION:
+                animateSuitableLocation(call, result);
+                break;
+        }
+    }
+
+    private void animateSuitableLocation(MethodCall call, MethodChannel.Result result) {
+        try {
+            Map<String, Object> arguments = call.arguments();
+            if (arguments == null) {
+                result.error("arguments is null", null, null);
+                return;
+            }
+            boolean isAnimateCamera = ParamUtil.getBoolean(arguments, "isAnimateCamera", true);
+            LogUtil.d(TAG, "isAnimateCamera===>" + isAnimateCamera);
+            String points = (String) arguments.get("points");
+            if (points == null || points.isEmpty()) {
+                result.error("latLngList is null", null, null);
+                return;
+            }
+            List<LatLng> latLngList = gson.fromJson(points, new TypeToken<List<LatLng>>() {
+            }.getType());
+            LogUtil.d(TAG, "latLngList===>" + latLngList);
+            if (latLngList == null || latLngList.isEmpty()) {
+                result.error("latLngList is empty", null, null);
+                return;
+            }
+            String paddingStr = (String) arguments.get("mapPadding");
+
+            MapPadding padding = null;
+            if (paddingStr != null && !paddingStr.isEmpty()) {
+                padding = gson.fromJson(paddingStr, MapPadding.class);
+            }
+            LogUtil.d(TAG, "padding===>" + padding);
+            moveToSuitableLocation(latLngList, padding);
+            result.success(null);
+        } catch (Exception e) {
+            LogUtil.d(TAG, "addPolyline error===>" + e.getMessage());
+            result.error("JsonSyntaxException", e.getMessage(), null);
         }
     }
 
+    private void moveToSuitableLocation(List<LatLng> points, MapPadding padding) {
+        if (map == null) {
+            return;
+        }
+        LatLngBounds.Builder builder = LatLngBounds.builder();
+        for (int i = 0; i < points.size(); i++) {
+            LatLng latLng = points.get(i);
+            builder.include(latLng);
+        }
+        LatLngBounds bounds = builder.build();
+        int left = 0;
+        int top = 0;
+        int right = 0;
+        int bottom = 0;
+        if (padding != null) {
+            left = (int) SizeUtil.dp2px(context, padding.getLeft());
+            top = (int) SizeUtil.dp2px(context, padding.getTop());
+            right = (int) SizeUtil.dp2px(context, padding.getRight());
+            bottom = (int) SizeUtil.dp2px(context, padding.getBottom());
+        }
+        LogUtil.d(TAG, "left===>" + left + "  top===>" + top + "  right===>" + right + "  bottom===>" + bottom);
+        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBoundsRect(bounds, left, right, top, bottom);
+        map.animateCamera(cameraUpdate);
+    }
+
+
     private void clearMap(MethodChannel.Result result) {
         LogUtil.i(TAG, "clearMap");
         map.clear();

+ 11 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/bean/MakerInfo.java

@@ -34,6 +34,16 @@ public class MakerInfo {
     @SerializedName("customAvatarUrl")
     private String customAvatarUrl;
 
+    @SerializedName("popup")
+    private Popup popup;
+
+    public Popup getPopup() {
+        return popup;
+    }
+
+    public void setPopup(Popup popup) {
+        this.popup = popup;
+    }
 
     public String getCustomAvatarUrl() {
         return customAvatarUrl;
@@ -115,6 +125,7 @@ public class MakerInfo {
                 ", markerType=" + markerType +
                 ", isSelected=" + isSelected +
                 ", customAvatarUrl='" + customAvatarUrl + '\'' +
+                ", popup=" + popup +
                 '}';
     }
 }

+ 56 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/bean/Popup.java

@@ -0,0 +1,56 @@
+package com.atmob.map_amap_android.bean;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.Objects;
+
+public class Popup {
+
+    @SerializedName("title")
+    private String title;
+
+    @SerializedName("desc")
+    private String desc;
+
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+
+    @Override
+    public String toString() {
+        return "Popup{" +
+                "title='" + title + '\'' +
+                ", desc='" + desc + '\'' +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Popup popup = (Popup) o;
+
+        return Objects.equals(title, popup.title) &&
+                Objects.equals(desc, popup.desc);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(title, desc);
+    }
+}

+ 2 - 1
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/contants/Constants.java

@@ -36,8 +36,9 @@ public class Constants {
     public static final String METHOD_MOVE_CAMERA = "map#moveCamera";
     public static final String METHOD_ANIMATE_CAMERA = "map#animateCamera";
     public static final String METHOD_MAP_CLEAR = "map#clear";
+    public static final String METHOD_MAP_MOVE_TO_SUITABLE_LOCATION = "map#moveToSuitableLocation";
 
-    public static final String[] METHOD_ID_LIST_FOR_MAP = {METHOD_MOVE_CAMERA, METHOD_ANIMATE_CAMERA,METHOD_MAP_CLEAR};
+    public static final String[] METHOD_ID_LIST_FOR_MAP = {METHOD_MOVE_CAMERA, METHOD_ANIMATE_CAMERA,METHOD_MAP_CLEAR,METHOD_MAP_MOVE_TO_SUITABLE_LOCATION};
 
     /**
      * markers

+ 48 - 26
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/marker/MarkersController.java

@@ -19,9 +19,11 @@ import com.amap.api.maps.model.Marker;
 import com.amap.api.maps.model.MarkerOptions;
 import com.atmob.map_amap_android.R;
 import com.atmob.map_amap_android.bean.MakerInfo;
+import com.atmob.map_amap_android.bean.Popup;
 import com.atmob.map_amap_android.contants.Constants;
 import com.atmob.map_amap_android.databinding.ItemLocationMarkerBinding;
 import com.atmob.map_amap_android.databinding.ItemTrackPassingMarkerBinding;
+import com.atmob.map_amap_android.databinding.ItemTrackSelectedMarkerBinding;
 import com.atmob.map_amap_android.databinding.ItemTrackStartMarkerBinding;
 import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
 import com.atmob.map_amap_android.util.BitmapCallback;
@@ -156,33 +158,42 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
             Object object = marker.getObject();
             if (object instanceof MakerInfo) {
                 MakerInfo cacheInfo = (MakerInfo) object;
-                boolean nameChanged = !Objects.equals(cacheInfo.getMarkerName(), makerInfo.getMarkerName());
-                boolean selectedChanged = cacheInfo.isSelected() != makerInfo.isSelected();
-                boolean avatarChanged = !Objects.equals(cacheInfo.getCustomAvatarUrl(), makerInfo.getCustomAvatarUrl());
-
-                if (nameChanged || selectedChanged || avatarChanged) {
-                    LogUtil.i(TAG, "updateMarkers==修改头像");
-                    ImageCacheLoader.loadBitmapAsync(context, makerInfo.getCustomAvatarUrl(), new BitmapCallback() {
-                        @Override
-                        public void onStartLoading() {
-                        }
-
-                        @Override
-                        public void onBitmapLoaded(Bitmap bitmap) {
-                            BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, bitmap);
-                            marker.setIcon(markerBitmap);
-                            LogUtil.i(TAG, "updateMarkers=成功修改头像:id=" + makerInfo.getId());
-                        }
-
-                        @Override
-                        public void onError(String errorMessage) {
-                            LogUtil.i(TAG, "updateMarkers==修改失败:" + errorMessage);
-                        }
-                    });
-                    marker.setObject(makerInfo);
+                if (cacheInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_SELECT_POINT) {
+                    boolean popupChanged = !Objects.equals(cacheInfo.getPopup(), makerInfo.getPopup());
+                    if (popupChanged) {
+                        BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, null);
+                        marker.setIcon(markerBitmap);
+                        marker.setZIndex(999);
+                        LogUtil.i(TAG, "updateMarkers=成功修改popup:" + makerInfo.getPopup());
+                    }
+                } else {
+                    boolean nameChanged = !Objects.equals(cacheInfo.getMarkerName(), makerInfo.getMarkerName());
+                    boolean selectedChanged = cacheInfo.isSelected() != makerInfo.isSelected();
+                    boolean avatarChanged = !Objects.equals(cacheInfo.getCustomAvatarUrl(), makerInfo.getCustomAvatarUrl());
+
+                    if (nameChanged || selectedChanged || avatarChanged) {
+                        LogUtil.i(TAG, "updateMarkers==修改头像");
+                        ImageCacheLoader.loadBitmapAsync(context, makerInfo.getCustomAvatarUrl(), new BitmapCallback() {
+                            @Override
+                            public void onStartLoading() {
+                            }
+
+                            @Override
+                            public void onBitmapLoaded(Bitmap bitmap) {
+                                BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, bitmap);
+                                marker.setIcon(markerBitmap);
+                                LogUtil.i(TAG, "updateMarkers=成功修改头像:id=" + makerInfo.getId());
+                            }
+
+                            @Override
+                            public void onError(String errorMessage) {
+                                LogUtil.i(TAG, "updateMarkers==修改失败:" + errorMessage);
+                            }
+                        });
+                    }
+                    marker.setZIndex(makerInfo.isSelected() ? 100 : 0);
                 }
-
-                marker.setZIndex(makerInfo.isSelected() ? 999 : 0);
+                marker.setObject(makerInfo);
             }
         } else {
             // 创建新 marker
@@ -226,6 +237,8 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
 
         if (makerInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_PASSING_POINT) {
             markerOption.anchor(0.5f, 0.5f);
+        } else if (makerInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_SELECT_POINT) {
+            markerOption.anchor(0.5f, 1.1f);
         }
 
         Marker newMarker = map.addMarker(markerOption);
@@ -280,6 +293,15 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
             ItemTrackPassingMarkerBinding trackPassingMarkerBinding = ItemTrackPassingMarkerBinding.inflate(LayoutInflater.from(context));
             ConstraintLayout view = trackPassingMarkerBinding.getRoot();
             return viewGetBitmapDescriptor(view);
+        } else if (markerInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_SELECT_POINT) {
+            ItemTrackSelectedMarkerBinding trackSelectedMarkerBinding = ItemTrackSelectedMarkerBinding.inflate(LayoutInflater.from(context));
+            Popup popup = markerInfo.getPopup();
+            if (popup != null) {
+                trackSelectedMarkerBinding.tvPopupTitle.setText(popup.getTitle());
+                trackSelectedMarkerBinding.tvPopupDesc.setText(popup.getDesc());
+            }
+            ConstraintLayout view = trackSelectedMarkerBinding.getRoot();
+            return viewGetBitmapDescriptor(view);
         }
         return null;
     }

BIN
plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/bg_track_popup_bubble.9.png


BIN
plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_track_stay.webp


+ 72 - 0
plugins/map_amap_android/android/src/main/res/layout/item_track_selected_marker.xml

@@ -0,0 +1,72 @@
+<?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="wrap_content"
+    android:layout_height="wrap_content">
+
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/ll_popup"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/bg_track_popup_bubble"
+        android:paddingHorizontal="12dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="26dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintWidth_max="170dp">
+
+
+        <TextView
+            android:id="@+id/tv_popup_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="14dp"
+            android:textColor="#333333"
+            android:textSize="11sp"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="广东省广州市天河区龙洞网鱼网咖网吧" />
+
+
+        <ImageView
+            android:id="@+id/iv_time_stay"
+            android:layout_width="12dp"
+            android:layout_height="12dp"
+            android:layout_marginTop="10dp"
+            android:src="@drawable/icon_track_stay"
+            app:layout_constraintStart_toStartOf="@+id/tv_popup_title"
+            app:layout_constraintTop_toBottomOf="@+id/tv_popup_title" />
+
+
+        <TextView
+            android:id="@+id/tv_popup_desc"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="2dp"
+            android:textColor="#666666"
+            android:textSize="10sp"
+            app:layout_constraintBottom_toBottomOf="@+id/iv_time_stay"
+            app:layout_constraintStart_toEndOf="@+id/iv_time_stay"
+            app:layout_constraintTop_toTopOf="@+id/iv_time_stay"
+            tools:text="11h30min" />
+
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+<!--    <View-->
+<!--        android:id="@+id/v_marker"-->
+<!--        android:layout_width="14dp"-->
+<!--        android:layout_height="14dp"-->
+<!--        android:background="@drawable/icon_marker_passing"-->
+<!--        app:layout_constraintBottom_toBottomOf="parent"-->
+<!--        app:layout_constraintEnd_toEndOf="parent"-->
+<!--        app:layout_constraintStart_toStartOf="parent"-->
+<!--        app:layout_constraintTop_toBottomOf="@+id/ll_popup" />-->
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>