Browse Source

[new]增加好友修改地图以及应用内更新头像

zk 5 months ago
parent
commit
6799eb665a

+ 1 - 1
android/build.gradle

@@ -1,7 +1,7 @@
 allprojects {
     ext {
         compileSdkVersion = 35
-        applicationId = "com.shishi.dingwei"
+        applicationId = "com.xjsucha.manbu"
         minSdkVersion = 23
         targetSdkVersion = 34
         ndkVersion = '27.0.12077973'

+ 5 - 1
lib/data/repositories/account_repository.dart

@@ -203,13 +203,17 @@ class AccountRepository {
     mineUserInfo.refresh();
   }
 
-  Future<void> userAvatarUpdate(String avatar) {
+  Future<bool> userAvatarUpdate(String avatar) {
+    if (avatar == mineUserInfo.value.avatar) {
+      return Future.value(true);
+    }
     return atmobApi
         .userAvatarUpdate(UserAvatarUpdateRequest(avatar))
         .then(HttpHandler.handle(true))
         .then((_) {
       updateAvatar(avatar);
       AtmobLog.d(tag, "userAvatarUpdate success: $avatar");
+      return true;
     });
   }
 

+ 4 - 0
lib/module/avatar/user_avatar_controller.dart

@@ -42,4 +42,8 @@ class UserAvatarController extends BaseController {
       ErrorHandler.toastError(error);
     });
   }
+
+  void onCloseClick() {
+    Get.back();
+  }
 }

+ 4 - 1
lib/module/avatar/user_avatar_view.dart

@@ -67,7 +67,10 @@ class UserAvatarView extends GetView<UserAvatarController> {
           style: TextStyle(fontSize: 14.sp, color: '#404040'.color),
         ),
         Spacer(),
-        Assets.images.iconAvatarClose.image(width: 25.w, height: 25.w),
+        GestureDetector(
+            onTap: controller.onCloseClick,
+            child:
+                Assets.images.iconAvatarClose.image(width: 25.w, height: 25.w)),
         SizedBox(width: 12.w),
       ],
     );

+ 5 - 6
lib/module/friend/friend_list_item.dart

@@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:location/resource/string.gen.dart';
 import 'package:location/utils/common_expand.dart';
+import 'package:location/utils/common_style.dart';
 
 import '../../data/bean/user_info.dart';
 import '../../data/consts/constants.dart';
@@ -35,12 +36,10 @@ Widget buildFriendItem(UserInfo userInfo, bool isMemberExpired,
             Row(
               children: [
                 SizedBox(width: 12.w),
-                Image.asset(
-                    userInfo.isMine == true
-                        ? Assets.images.iconDefaultMineAvatar.path
-                        : Assets.images.iconDefaultFriendAvatar.path,
-                    width: 48.w,
-                    height: 48.w),
+                buildCustomAvatarOrDefaultAvatarView(
+                    size: 48.w,
+                    avatar: userInfo.avatar,
+                    isMine: userInfo.isMine == true),
                 SizedBox(width: 8.w),
                 Expanded(
                   child: Column(

+ 19 - 1
lib/module/main/main_controller.dart

@@ -21,6 +21,7 @@ import 'package:location/module/news/news_page.dart';
 import 'package:location/module/urgent_contact/urgent_contact_page.dart';
 import 'package:location/resource/string.gen.dart';
 import 'package:location/sdk/map/map_helper.dart';
+import 'package:location/utils/atmob_log.dart';
 import 'package:location/utils/mmkv_util.dart';
 import 'package:location/utils/toast_util.dart';
 
@@ -58,6 +59,7 @@ class MainController extends BaseController {
 
   final MapController mapController = MapController();
 
+  StreamSubscription? mineUserInfoSubscription;
   StreamSubscription? mineLocationSubscription;
   StreamSubscription? friendsListSubscription;
   StreamSubscription? loginSubscription;
@@ -121,6 +123,11 @@ class MainController extends BaseController {
       }
       _autoSelectFriend();
     });
+    updateMineInfo(accountRepository.mineUserInfo.value);
+    mineUserInfoSubscription =
+        accountRepository.mineUserInfo.listen((mineInfo) {
+      updateMineInfo(mineInfo);
+    });
     mineLocationSubscription =
         accountRepository.mineUserInfo.value.lastLocation.listen((location) {
       final mineInfo = accountRepository.mineUserInfo.value;
@@ -135,6 +142,17 @@ class MainController extends BaseController {
     friendsRepository.refreshFriends();
   }
 
+  void updateMineInfo(UserInfo mineInfo) {
+    if (mineInfo == selectedFriend) {
+      _selectedFriend.refresh();
+    }
+    if (mineInfo.lastLocation.value == null) {
+      return;
+    }
+    mapController.updateOrAddMarker(Location2MarkerUtil.userInfo2Marker(
+        mineInfo, selectedFriend?.id == mineInfo.id));
+  }
+
   Future<void> onAddFriendClick() {
     return AddFriendPage.show();
   }
@@ -277,7 +295,6 @@ class MainController extends BaseController {
     FriendPage.start();
   }
 
-
   void onViewTraceClick(UserInfo userInfo) {
     print("userInfosfsdf--${userInfo}");
     if (!accountRepository.isLogin.value) {
@@ -394,5 +411,6 @@ class MainController extends BaseController {
     friendsListSubscription?.cancel();
     loginSubscription?.cancel();
     memberStatusInfoSubscription?.cancel();
+    mineUserInfoSubscription?.cancel();
   }
 }

+ 4 - 101
lib/module/main/main_friend_item.dart

@@ -4,12 +4,8 @@ import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:location/data/bean/user_info.dart';
-import 'package:location/data/consts/constants.dart';
-import 'package:location/resource/assets.gen.dart';
 import 'package:location/resource/colors.gen.dart';
-import 'package:location/resource/string.gen.dart';
 import 'package:location/utils/common_expand.dart';
-import 'package:location/widget/relative_time_text.dart';
 import '../../utils/common_style.dart';
 import '../../utils/common_util.dart';
 import '../../widget/marquee_text.dart';
@@ -32,12 +28,10 @@ Widget mainFriendItem(UserInfo userInfo, bool isSelected,
               width: 52.w,
               height: 52.w,
               child: Center(
-                  child: Image.asset(
-                      userInfo.isMine == true
-                          ? Assets.images.iconDefaultMineAvatar.path
-                          : Assets.images.iconDefaultFriendAvatar.path,
-                      width: 44.w,
-                      height: 44.w))),
+                  child: buildCustomAvatarOrDefaultAvatarView(
+                      size: 44.w,
+                      avatar: userInfo.avatar,
+                      isMine: userInfo.isMine == true))),
           Positioned(
             left: 3.w,
             bottom: 0,
@@ -73,94 +67,3 @@ Widget mainFriendItem(UserInfo userInfo, bool isSelected,
   );
 }
 
-Widget mainSelectedFriendItem(UserInfo userInfo, bool isMemberExpired,
-    {VoidCallback? viewTraceClick}) {
-  return Container(
-      width: 336.w,
-      height: 86.w,
-      decoration: BoxDecoration(
-          color: ColorName.white,
-          borderRadius: BorderRadius.all(Radius.circular(20.w))),
-      child: Row(
-        children: [
-          SizedBox(width: 7.w),
-          Image(
-              image: userInfo.isMine == true
-                  ? Assets.images.iconDefaultMineAvatar.provider()
-                  : Assets.images.iconDefaultFriendAvatar.provider(),
-              width: 50.w,
-              height: 50.w),
-          SizedBox(width: 5.w),
-          Expanded(
-            child: Container(
-              margin: EdgeInsets.symmetric(vertical: 15.w),
-              child: Column(
-                mainAxisAlignment: MainAxisAlignment.center,
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  Row(
-                    children: [
-                      Text(
-                        userInfo.getUserNickName(),
-                        style: TextStyle(
-                            fontWeight: FontWeight.bold,
-                            fontSize: 16.sp,
-                            color: '#202020'.color),
-                      ),
-                      SizedBox(width: 7.w),
-                      RelativeTimeText(
-                          timestamp:
-                              userInfo.lastLocation.value?.lastUpdateTime,
-                          updateInterval: Duration(minutes: 1),
-                          style: TextStyle(
-                              fontSize: 12.sp, color: '#A7A7A7'.color)),
-                      Spacer(),
-                      GestureDetector(
-                        onTap: () {
-                          viewTraceClick?.call();
-                        },
-                        child: Container(
-                            margin: EdgeInsets.only(right: 16.w),
-                            decoration: getPrimaryBtnDecoration(32.w),
-                            padding: EdgeInsets.symmetric(
-                                horizontal: 21.w, vertical: 5.w),
-                            child: Text(StringName.locationTrace,
-                                style: TextStyle(
-                                    fontSize: 15.sp, color: Colors.white))),
-                      )
-                    ],
-                  ),
-                  Expanded(
-                    child: Container(
-                      margin: EdgeInsets.only(right: 17.w),
-                      child: ImageFiltered(
-                        enabled: userInfo.blockedMe == true ||
-                            (isMemberExpired && !(userInfo.isMine == true)),
-                        imageFilter: ImageFilter.blur(
-                          sigmaX: Constants.blurredX,
-                          sigmaY: Constants.blurredY,
-                        ),
-                        child: userInfo.blockedMe == true ||
-                                (isMemberExpired && !(userInfo.isMine == true))
-                            ? Text(
-                                addressCheck(
-                                    userInfo.lastLocation.value?.address),
-                                style: TextStyle(
-                                    fontSize: 13.sp, color: ColorName.black50))
-                            : MarqueeText.marquee(
-                                text: addressCheck(
-                                    userInfo.lastLocation.value?.address),
-                                textStyle: TextStyle(
-                                    fontSize: 13.sp, color: ColorName.black50),
-                                containerWidth: 244.w),
-                      ),
-                    ),
-                  )
-                  // Text('广东省广州市天河区XX街街XX街区XX村XX')
-                ],
-              ),
-            ),
-          )
-        ],
-      ));
-}

+ 118 - 17
lib/module/main/main_page.dart

@@ -1,3 +1,5 @@
+import 'dart:ui';
+
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_map/flutter_map.dart';
@@ -12,8 +14,12 @@ import 'package:location/resource/colors.gen.dart';
 import 'package:location/resource/string.gen.dart';
 import 'package:location/utils/common_expand.dart';
 import 'package:sliding_sheet2/sliding_sheet2.dart';
-
+import '../../data/consts/constants.dart';
 import '../../router/app_pages.dart';
+import '../../utils/common_style.dart';
+import '../../utils/common_util.dart';
+import '../../widget/marquee_text.dart';
+import '../../widget/relative_time_text.dart';
 import 'main_friend_item.dart';
 
 class MainPage extends BasePage<MainController> {
@@ -290,22 +296,6 @@ class MainPage extends BasePage<MainController> {
     );
   }
 
-  Widget buildSelectFriendInfoView() {
-    return Obx(() {
-      UserInfo? userInfo = controller.selectedFriend;
-      if (userInfo == null) {
-        return SizedBox(
-          height: 86.w,
-        );
-      }
-      return mainSelectedFriendItem(
-          userInfo,
-          (controller.memberStatusInfo.value == null ||
-              controller.memberStatusInfo.value?.expired == true),
-          viewTraceClick: () => controller.onViewTraceClick(userInfo));
-    });
-  }
-
   Widget buildMainFriendList() {
     return SizedBox(
       height: 58.w,
@@ -353,4 +343,115 @@ class MainPage extends BasePage<MainController> {
       ),
     );
   }
+
+  Widget buildSelectFriendInfoView() {
+    return Container(
+        width: 336.w,
+        height: 86.w,
+        decoration: BoxDecoration(
+            color: ColorName.white,
+            borderRadius: BorderRadius.all(Radius.circular(20.w))),
+        child: Row(
+          children: [
+            SizedBox(width: 7.w),
+            Obx(() {
+              return buildCustomAvatarOrDefaultAvatarView(
+                  size: 50.w,
+                  avatar: controller.selectedFriend?.avatar,
+                  isMine: controller.selectedFriend?.isMine == true);
+            }),
+            SizedBox(width: 5.w),
+            Expanded(
+              child: Container(
+                margin: EdgeInsets.symmetric(vertical: 15.w),
+                child: Column(
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    Row(
+                      children: [
+                        Obx(() {
+                          return Text(
+                            controller.selectedFriend?.getUserNickName() ?? '',
+                            style: TextStyle(
+                                fontWeight: FontWeight.bold,
+                                fontSize: 16.sp,
+                                color: '#202020'.color),
+                          );
+                        }),
+                        SizedBox(width: 7.w),
+                        Obx(() {
+                          return RelativeTimeText(
+                              timestamp: controller.selectedFriend?.lastLocation
+                                  .value?.lastUpdateTime,
+                              updateInterval: Duration(minutes: 1),
+                              style: TextStyle(
+                                  fontSize: 12.sp, color: '#A7A7A7'.color));
+                        }),
+                        Spacer(),
+                        Obx(() {
+                          return controller.selectedFriend != null
+                              ? GestureDetector(
+                                  onTap: () => controller.onViewTraceClick(
+                                      controller.selectedFriend!),
+                                  child: Container(
+                                      margin: EdgeInsets.only(right: 16.w),
+                                      decoration: getPrimaryBtnDecoration(32.w),
+                                      padding: EdgeInsets.symmetric(
+                                          horizontal: 21.w, vertical: 5.w),
+                                      child: Text(StringName.locationTrace,
+                                          style: TextStyle(
+                                              fontSize: 15.sp,
+                                              color: Colors.white))),
+                                )
+                              : SizedBox.shrink();
+                        }),
+                      ],
+                    ),
+                    Expanded(
+                      child: Container(
+                        margin: EdgeInsets.only(right: 17.w),
+                        child: Obx(() {
+                          return ImageFiltered(
+                            enabled: controller.selectedFriend?.blockedMe ==
+                                    true ||
+                                ((controller.memberStatusInfo.value == null ||
+                                        controller.memberStatusInfo.value
+                                                ?.expired ==
+                                            true) &&
+                                    !(controller.selectedFriend?.isMine ==
+                                        true)),
+                            imageFilter: ImageFilter.blur(
+                              sigmaX: Constants.blurredX,
+                              sigmaY: Constants.blurredY,
+                            ),
+                            child: controller.selectedFriend?.blockedMe == true ||
+                                    ((controller.memberStatusInfo.value == null ||
+                                            controller.memberStatusInfo.value?.expired ==
+                                                true) &&
+                                        !(controller.selectedFriend?.isMine ==
+                                            true))
+                                ? Text(addressCheck(controller.selectedFriend?.lastLocation.value?.address),
+                                    style: TextStyle(
+                                        fontSize: 13.sp,
+                                        color: ColorName.black50))
+                                : MarqueeText.marquee(
+                                    text: addressCheck(controller.selectedFriend
+                                        ?.lastLocation.value?.address),
+                                    textStyle: TextStyle(
+                                        fontSize: 13.sp,
+                                        color: ColorName.black50),
+                                    containerWidth: 244.w),
+                          );
+                        }),
+                      ),
+                    )
+                    // Text('广东省广州市天河区XX街街XX街区XX村XX')
+                  ],
+                ),
+              ),
+            )
+          ],
+        ));
+  }
 }

+ 4 - 3
lib/module/mine/mine_page.dart

@@ -11,6 +11,7 @@ import 'package:location/resource/string.gen.dart';
 import 'package:location/utils/common_expand.dart';
 
 import '../../router/app_pages.dart';
+import '../../utils/common_style.dart';
 import '../../utils/date_util.dart';
 import '../../widget/common_view.dart';
 import 'mine_controller.dart';
@@ -450,9 +451,9 @@ class MinePage extends BasePage<MineController> {
           width: 1.w,
         ),
       ),
-      child: ClipOval(
-        child: CachedNetworkImage(
-            width: 54.w, height: 54.w, imageUrl: avatar, fit: BoxFit.cover),
+      child: buildCustomAvatarView(
+        size: 54.w,
+        avatar: avatar,
       ),
     );
   }

+ 28 - 0
lib/utils/common_style.dart

@@ -1,9 +1,37 @@
+import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:location/resource/colors.gen.dart';
 
+import '../resource/assets.gen.dart';
+
 Decoration getPrimaryBtnDecoration(double radius) {
   return BoxDecoration(
     color: ColorName.colorPrimary,
     borderRadius: BorderRadius.circular(radius),
   );
 }
+
+Widget buildCustomAvatarView({required double size, required String avatar}) {
+  return SizedBox(
+    width: size,
+    height: size,
+    child: ClipOval(
+      child: CachedNetworkImage(imageUrl: avatar, fit: BoxFit.cover),
+    ),
+  );
+}
+
+Widget buildCustomAvatarOrDefaultAvatarView({
+  required double size,
+  required String? avatar,
+  required bool isMine,
+}) {
+  return avatar != null
+      ? buildCustomAvatarView(avatar: avatar, size: size)
+      : Image.asset(
+          (isMine == true
+              ? Assets.images.iconDefaultMineAvatar.path
+              : Assets.images.iconDefaultFriendAvatar.path),
+          width: size,
+          height: size);
+}

+ 3 - 6
lib/utils/location_convert_marker_util.dart

@@ -15,11 +15,12 @@ class Location2MarkerUtil {
           ? MarkerType.mine
           : MarkerType.friend,
       isSelected: isSelected,
+      customAvatarUrl: e.avatar,
     );
   }
 
-  static List<Marker> userInfoList2MarkerList(List<UserInfo> list,
-      bool isMemberExpired, UserInfo? selectedFriend) {
+  static List<Marker> userInfoList2MarkerList(
+      List<UserInfo> list, bool isMemberExpired, UserInfo? selectedFriend) {
     List<Marker> markers = [];
     for (var e in list) {
       if (e.isMine == true) {
@@ -32,9 +33,5 @@ class Location2MarkerUtil {
       }
     }
     return markers;
-    // return list
-    //     .where((e) => e.blockedHim != true)
-    //     .map((e) => userInfo2Marker(e, e.id == selectedFriend?.id))
-    //     .toList();
   }
 }

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

@@ -14,6 +14,8 @@ class Marker implements Codable {
 
   bool isSelected;
 
+  String? customAvatarUrl;
+
   Marker({
     required this.id,
     required this.markerName,
@@ -21,6 +23,7 @@ class Marker implements Codable {
     required this.latitude,
     required this.markerType,
     this.isSelected = false,
+    this.customAvatarUrl,
   });
 
   @override
@@ -32,6 +35,7 @@ class Marker implements Codable {
       'latitude': latitude,
       'markerType': markerType.value,
       'isSelected': isSelected,
+      'customAvatarUrl': customAvatarUrl,
     };
   }
 
@@ -43,6 +47,7 @@ class Marker implements Codable {
       latitude: map['latitude'],
       markerType: MarkerType.fromValue(map['markerType'] as int),
       isSelected: map['isSelected'],
+      customAvatarUrl: map['customAvatarUrl'] as String?,
     );
   }
 }

+ 15 - 1
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/bean/MakerInfo.java

@@ -31,6 +31,18 @@ public class MakerInfo {
     @SerializedName("isSelected")
     private boolean isSelected;
 
+    @SerializedName("customAvatarUrl")
+    private String customAvatarUrl;
+
+
+    public String getCustomAvatarUrl() {
+        return customAvatarUrl;
+    }
+
+    public void setCustomAvatarUrl(String customAvatarUrl) {
+        this.customAvatarUrl = customAvatarUrl;
+    }
+
     public boolean isSelected() {
         return isSelected;
     }
@@ -80,13 +92,14 @@ public class MakerInfo {
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({MarkerType.MINE, MarkerType.FRIEND, MarkerType.TRACE_START_POINT, MarkerType.TRACE_END_FRIEND_POINT, MarkerType.TRACE_END_MINE_POINT})
+    @IntDef({MarkerType.MINE, MarkerType.FRIEND, MarkerType.TRACE_START_POINT, MarkerType.TRACE_END_FRIEND_POINT, MarkerType.TRACE_END_MINE_POINT,MarkerType.TRACE_SELECT_POINT})
     public @interface MarkerType {
         int MINE = 1;
         int FRIEND = 2;
         int TRACE_START_POINT = 3;
         int TRACE_END_FRIEND_POINT = 4;
         int TRACE_END_MINE_POINT = 5;
+        int TRACE_SELECT_POINT = 6;
     }
 
 
@@ -100,6 +113,7 @@ public class MakerInfo {
                 ", latitude=" + latitude +
                 ", markerType=" + markerType +
                 ", isSelected=" + isSelected +
+                ", customAvatarUrl='" + customAvatarUrl + '\'' +
                 '}';
     }
 }

+ 91 - 32
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/marker/MarkersController.java

@@ -22,7 +22,9 @@ import com.atmob.map_amap_android.contants.Constants;
 import com.atmob.map_amap_android.databinding.ItemLocationMarkerBinding;
 import com.atmob.map_amap_android.databinding.ItemTrackStartMarkerBinding;
 import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
+import com.atmob.map_amap_android.util.BitmapCallback;
 import com.atmob.map_amap_android.util.GsonUtil;
+import com.atmob.map_amap_android.util.ImageCacheLoader;
 import com.atmob.map_amap_android.util.LogUtil;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
@@ -142,46 +144,91 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
     }
 
 
-    private void updateMarker(MakerInfo makerInfo) {
-        LatLng latLng = new LatLng(makerInfo.getLatitude(), makerInfo.getLongitude());
-        Marker marker = currentMarkers.get(makerInfo.getId());
-        LogUtil.d(TAG, "updateMarkers==>" + makerInfo);
+    private void updateMarker(final MakerInfo makerInfo) {
+        final LatLng latLng = new LatLng(makerInfo.getLatitude(), makerInfo.getLongitude());
+        final Marker marker = currentMarkers.get(makerInfo.getId());
+
         if (marker != null) {
             marker.setPosition(latLng);
+
             Object object = marker.getObject();
             if (object instanceof MakerInfo) {
                 MakerInfo cacheInfo = (MakerInfo) object;
-                if (!Objects.equals(cacheInfo.getMarkerName(), makerInfo.getMarkerName())
-                        || cacheInfo.isSelected() != makerInfo.isSelected()) {
-                    BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo);
-                    marker.setIcon(markerBitmap);
+                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 (makerInfo.isSelected()) {
-                    marker.setZIndex(999);
-                } else {
-                    marker.setZIndex(0);
-                }
+
+                marker.setZIndex(makerInfo.isSelected() ? 999 : 0);
             }
-            LogUtil.i(TAG, "updateMarkers=marker..updateSuccess,id=" + makerInfo.getId());
         } else {
-            BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo);
-            MarkerOptions markerOption = new MarkerOptions()
-                    .position(latLng)
-                    .icon(markerBitmap)
-                    .anchor(0.5f, 0.9f);
-            marker = map.addMarker(markerOption);
-            marker.setObject(makerInfo);
-            if (makerInfo.isSelected()) {
-                marker.setZIndex(999);
-            } else {
-                marker.setZIndex(0);
-            }
-            currentMarkers.put(makerInfo.getId(), marker);
-            LogUtil.i(TAG, "updateMarkers=marker..addSuccess,id=" + makerInfo.getId());
+            // 创建新 marker
+            loadAvatarAndCreateMarker(makerInfo, latLng);
         }
     }
 
+    private void loadAvatarAndCreateMarker(final MakerInfo makerInfo, final LatLng latLng) {
+        if (TextUtils.isEmpty(makerInfo.getCustomAvatarUrl())) {
+            // 无头像,使用默认图标
+            addMarkerToMap(makerInfo, latLng, null);
+        } else {
+            // 加载头像后再添加 marker
+            ImageCacheLoader.loadBitmapAsync(context, makerInfo.getCustomAvatarUrl(), new BitmapCallback() {
+                @Override
+                public void onStartLoading() {}
+
+                @Override
+                public void onBitmapLoaded(Bitmap bitmap) {
+                    addMarkerToMap(makerInfo, latLng, bitmap);
+                    LogUtil.i(TAG, "updateMarkers=成功修改头像:id=" + makerInfo.getId());
+                }
+
+                @Override
+                public void onError(String errorMessage) {
+                    addMarkerToMap(makerInfo, latLng, null);
+                    LogUtil.i(TAG, "updateMarkers=使用默认头像:id=" + makerInfo.getId() + ",errorMessage:" + errorMessage);
+                }
+            });
+        }
+    }
+
+
+
+    private void addMarkerToMap(MakerInfo makerInfo, LatLng latLng, Bitmap avatarBitmap) {
+        BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, avatarBitmap);
+        MarkerOptions markerOption = new MarkerOptions()
+                .position(latLng)
+                .icon(markerBitmap)
+                .anchor(0.5f, 0.9f);
+
+        Marker newMarker = map.addMarker(markerOption);
+        newMarker.setObject(makerInfo);
+        newMarker.setZIndex(makerInfo.isSelected() ? 999 : 0);
+        currentMarkers.put(makerInfo.getId(), newMarker);
+    }
+
+
+
 
     @Override
     public String[] getRegisterMethodIdArray() {
@@ -189,15 +236,23 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
     }
 
 
-    public BitmapDescriptor getMarkerBitmap(MakerInfo markerInfo) {
+    public BitmapDescriptor getMarkerBitmap(MakerInfo markerInfo, Bitmap customAvatarBitmap) {
         if (markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE || markerInfo.getMarkerType() == MakerInfo.MarkerType.FRIEND) {
             if (markerBinding == null) {
                 markerBinding = ItemLocationMarkerBinding.inflate(LayoutInflater.from(context));
             }
             ConstraintLayout view = markerBinding.getRoot();
             markerBinding.locationMarkerTvName.setText(markerInfo.getMarkerName());
-            markerBinding.ivMarkerAvatar.setImageResource(markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE ? R.drawable.icon_default_mine_avatar : R.drawable.icon_default_friend_avatar);
-            markerBinding.vBottom.setBackgroundResource(markerInfo.isSelected() ? R.drawable.icon_marker_selected_bottom_circle : R.drawable.icon_marker_normal_bottom_circle);
+            if (customAvatarBitmap == null) {
+                markerBinding.ivMarkerAvatar.setImageResource(markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE ? R.drawable.icon_default_mine_avatar : R.drawable.icon_default_friend_avatar);
+            } else {
+                markerBinding.ivMarkerAvatar.setImageBitmap(customAvatarBitmap);
+            }
+            if(markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE){
+                markerBinding.vBottom.setBackgroundResource(markerInfo.isSelected() ? R.drawable.icon_mine_marker_selected_bottom_circle : R.drawable.icon_mine_marker_normal_bottom_circle);
+            }else{
+                markerBinding.vBottom.setBackgroundResource(markerInfo.isSelected() ? R.drawable.icon_marker_selected_bottom_circle : R.drawable.icon_marker_normal_bottom_circle);
+            }
             markerBinding.locationMarkerIvBg.setImageResource(markerInfo.isSelected() ? (markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE ? R.drawable.icon_location_mine_marker_selected : R.drawable.icon_location_marker_selected) : R.drawable.icon_location_marker_normal);
             return viewGetBitmapDescriptor(view);
         } else if (markerInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_START_POINT) {
@@ -208,7 +263,11 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
             ItemLocationMarkerBinding trackEndPointMarkerBinding = ItemLocationMarkerBinding.inflate(LayoutInflater.from(context));
             ConstraintLayout view = trackEndPointMarkerBinding.getRoot();
             trackEndPointMarkerBinding.locationMarkerTvName.setText(markerInfo.getMarkerName());
-            trackEndPointMarkerBinding.ivMarkerAvatar.setImageResource(markerInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_END_MINE_POINT ? R.drawable.icon_default_mine_avatar : R.drawable.icon_default_friend_avatar);
+            if (customAvatarBitmap == null) {
+                trackEndPointMarkerBinding.ivMarkerAvatar.setImageResource(markerInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_END_MINE_POINT ? R.drawable.icon_default_mine_avatar : R.drawable.icon_default_friend_avatar);
+            } else {
+                trackEndPointMarkerBinding.ivMarkerAvatar.setImageBitmap(customAvatarBitmap);
+            }
             trackEndPointMarkerBinding.locationMarkerIvBg.setImageResource(R.drawable.icon_location_marker_normal);
             trackEndPointMarkerBinding.vBottom.setBackgroundResource(R.drawable.icon_marker_track_bottom_circle);
             return viewGetBitmapDescriptor(view);

+ 11 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/BitmapCallback.java

@@ -0,0 +1,11 @@
+package com.atmob.map_amap_android.util;
+
+import android.graphics.Bitmap;
+
+public interface BitmapCallback {
+    void onBitmapLoaded(Bitmap bitmap);
+
+    void onStartLoading();
+
+    void onError(String errorMessage);
+}

+ 107 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/ImageCacheLoader.java

@@ -0,0 +1,107 @@
+package com.atmob.map_amap_android.util;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.LruCache;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class ImageCacheLoader {
+
+    private static final Handler handler = new Handler(Looper.getMainLooper());
+
+    // 内存缓存
+    private static final LruCache<String, Bitmap> memoryCache = new LruCache<String, Bitmap>((int) (Runtime.getRuntime().maxMemory() / 8 / 1024)) {
+        @Override
+        protected int sizeOf(String key, Bitmap value) {
+            return value.getByteCount() / 1024;
+        }
+    };
+
+    // 同步加载(阻塞,不能在主线程调用)
+    public static Bitmap loadBitmap(Context context, String imageUrl) {
+        String key = String.valueOf(imageUrl.hashCode());
+
+        // 1. 内存缓存
+        Bitmap memoryBitmap = memoryCache.get(key);
+        if (memoryBitmap != null) {
+            return memoryBitmap;
+        }
+
+        // 2. 磁盘缓存
+        File cacheDir = new File(context.getCacheDir(), "image_cache");
+        if (!cacheDir.exists()) {
+            cacheDir.mkdirs();
+        }
+        File imageFile = new File(cacheDir, key + ".png");
+
+        if (imageFile.exists()) {
+            Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
+            memoryCache.put(key, bitmap);
+            return bitmap;
+        }
+
+        // 3. 网络下载
+        Bitmap bitmap = downloadImage(imageUrl);
+        if (bitmap != null) {
+            try {
+                FileOutputStream fos = new FileOutputStream(imageFile);
+                bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
+                fos.close();
+                memoryCache.put(key, bitmap);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return bitmap;
+    }
+
+    public static void loadBitmapAsync(final Context context, final String imageUrl, final BitmapCallback callback) {
+        // 通知:开始加载
+       handler.post(callback::onStartLoading);
+
+        new Thread(() -> {
+            try {
+                final Bitmap bitmap = loadBitmap(context, imageUrl);
+                handler.post(() -> {
+                    if (bitmap != null) {
+                        callback.onBitmapLoaded(bitmap);
+                    } else {
+                        callback.onError("图片加载失败,Bitmap 为 null");
+                    }
+                });
+            } catch (Exception e) {
+                e.printStackTrace();
+                final String message = e.getMessage() != null ? e.getMessage() : "未知错误";
+                handler.post(() -> callback.onError("图片加载异常: " + message));
+            }
+        }).start();
+    }
+
+
+    // 下载图片
+    private static Bitmap downloadImage(String urlStr) {
+        try {
+            URL url = new URL(urlStr);
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setConnectTimeout(5000);
+            conn.setReadTimeout(5000);
+            conn.connect();
+
+            InputStream inputStream = conn.getInputStream();
+            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
+            inputStream.close();
+            return bitmap;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

+ 9 - 0
plugins/map_amap_android/android/src/main/res/drawable/icon_mine_marker_normal_bottom_circle.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <corners android:radius="100dp" />
+    <solid android:color="#4A7BFF" />
+    <stroke
+        android:width="2dp"
+        android:color="#fff" />
+</shape>

+ 9 - 0
plugins/map_amap_android/android/src/main/res/drawable/icon_mine_marker_selected_bottom_circle.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <corners android:radius="100dp" />
+    <solid android:color="#fff" />
+    <stroke
+        android:width="2dp"
+        android:color="#4A7BFF" />
+</shape>