Forráskód Böngészése

[new]新增选中时显示好友最后上报电量

zk 4 hónapja
szülő
commit
24fe5e0da6

+ 5 - 0
lib/data/api/atmob_api.dart

@@ -27,6 +27,7 @@ import 'package:location/data/api/response/configs_response.dart';
 import 'package:location/data/api/response/contact_list_response.dart';
 import 'package:location/data/api/response/contact_may_day_all_response.dart';
 import 'package:location/data/api/response/daily_keyword_response.dart';
+import 'package:location/data/api/response/electric_query_response.dart';
 import 'package:location/data/api/response/friends_list_response.dart';
 import 'package:location/data/api/response/item_list_response.dart';
 import 'package:location/data/api/response/location_track_days_response.dart';
@@ -255,4 +256,8 @@ abstract class AtmobApi {
   @POST("/s/v1/location/track/daily/interpret")
   Future<BaseResponse<TrackDailyInterpretResponse>> trackDailyInterpret(
       @Body() QueryTrackRequest request);
+
+  @POST("/s/v1/user/electric/query")
+  Future<BaseResponse<ElectricQueryResponse>> userElectricQuery(
+      @Body() FriendsOperationRequest request);
 }

+ 39 - 0
lib/data/api/atmob_api.g.dart

@@ -1666,6 +1666,45 @@ class _AtmobApi implements AtmobApi {
     return _value;
   }
 
+  @override
+  Future<BaseResponse<ElectricQueryResponse>> userElectricQuery(
+      FriendsOperationRequest request) async {
+    final _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _headers = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
+    final _options =
+        _setStreamType<BaseResponse<ElectricQueryResponse>>(Options(
+      method: 'POST',
+      headers: _headers,
+      extra: _extra,
+    )
+            .compose(
+              _dio.options,
+              '/s/v1/user/electric/query',
+              queryParameters: queryParameters,
+              data: _data,
+            )
+            .copyWith(
+                baseUrl: _combineBaseUrls(
+              _dio.options.baseUrl,
+              baseUrl,
+            )));
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<ElectricQueryResponse> _value;
+    try {
+      _value = BaseResponse<ElectricQueryResponse>.fromJson(
+        _result.data!,
+        (json) => ElectricQueryResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
   RequestOptions newRequestOptions(Object? options) {
     if (options is RequestOptions) {
       return options as RequestOptions;

+ 14 - 0
lib/data/api/response/electric_query_response.dart

@@ -0,0 +1,14 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'electric_query_response.g.dart';
+
+@JsonSerializable()
+class ElectricQueryResponse {
+  @JsonKey(name: "electric")
+  int? electric;
+
+  ElectricQueryResponse({this.electric});
+
+  factory ElectricQueryResponse.fromJson(Map<String, dynamic> json) =>
+      _$ElectricQueryResponseFromJson(json);
+}

+ 19 - 0
lib/data/api/response/electric_query_response.g.dart

@@ -0,0 +1,19 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'electric_query_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ElectricQueryResponse _$ElectricQueryResponseFromJson(
+        Map<String, dynamic> json) =>
+    ElectricQueryResponse(
+      electric: (json['electric'] as num?)?.toInt(),
+    );
+
+Map<String, dynamic> _$ElectricQueryResponseToJson(
+        ElectricQueryResponse instance) =>
+    <String, dynamic>{
+      'electric': instance.electric,
+    };

+ 2 - 2
lib/module/main/main_controller.dart

@@ -236,7 +236,7 @@ class MainController extends BaseController {
 
   void onSelectUserClick(UserInfo userInfo) {
     KVUtil.putString(Constants.keyLastSelectFriendId, userInfo.id);
-    if (_selectedFriend.value == userInfo) {
+    if (userInfo == _selectedFriend.value) {
       return;
     }
     _setSelectUserInfo(userInfo);
@@ -289,7 +289,7 @@ class MainController extends BaseController {
   void _updateMapSelected(UserInfo? oldInfo, UserInfo newInfo,
       {int? electric}) {
     List<UserInfo> markers = [];
-    if (oldInfo != null) {
+    if (oldInfo != null && oldInfo.id != newInfo.id) {
       markers.add(oldInfo);
     }
     markers.add(newInfo);

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

@@ -1,5 +1,6 @@
 package com.atmob.map_amap_android.overlays.marker;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -18,8 +19,8 @@ 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.ItemElectricMarkerBinding;
 import com.atmob.map_amap_android.databinding.ItemLocationMarkerBinding;
 import com.atmob.map_amap_android.databinding.ItemTrackErrorMarkerBinding;
 import com.atmob.map_amap_android.databinding.ItemTrackPassingMarkerBinding;
@@ -56,6 +57,7 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
     private final HashMap<String, Marker> currentMarkers = new HashMap<>(10);
 
     private ItemLocationMarkerBinding markerBinding;
+    private ItemElectricMarkerBinding electricMarkerBinding;
 
     private final Gson gson;
 
@@ -86,7 +88,7 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
 
 
     private void removeMarker(MethodCall call, MethodChannel.Result result) {
-        LogUtil.i(TAG, "removeMarker===>" + call.arguments());
+        LogUtil.d(TAG, "removeMarker===>" + call.arguments());
         Map<String, Object> arguments = call.arguments();
         if (arguments == null || arguments.isEmpty()) {
             result.error("-1", "removeMarker.arguments is empty", null);
@@ -97,13 +99,13 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
         if (marker != null) {
             marker.remove();
             currentMarkers.remove(markerId);
-            LogUtil.i(TAG, "removeMarker===>成功删除marker:" + markerId);
+            LogUtil.d(TAG, "removeMarker===>成功删除marker:" + markerId);
         }
         result.success(null);
     }
 
     private void replaceAllMarkers(MethodCall call, MethodChannel.Result result) {
-        LogUtil.i(TAG, "replaceAllMarkers===>" + call.arguments());
+        LogUtil.d(TAG, "replaceAllMarkers===>" + call.arguments());
         try {
             String arguments = call.arguments();
             List<MakerInfo> list = null;
@@ -152,7 +154,7 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
     }
 
     private void updateMarkers(MethodCall call, MethodChannel.Result result) {
-        LogUtil.i(TAG, "updateMarkers===>" + call.arguments());
+        LogUtil.d(TAG, "updateMarkers===>" + call.arguments());
         String arguments = call.arguments();
         if (arguments == null || TextUtils.isEmpty(arguments)) {
             result.error("-1", "updateMarkers.arguments is empty", null);
@@ -189,9 +191,10 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
                     boolean nameChanged = !Objects.equals(cacheInfo.getMarkerName(), makerInfo.getMarkerName());
                     boolean selectedChanged = cacheInfo.isSelected() != makerInfo.isSelected();
                     boolean avatarChanged = !Objects.equals(cacheInfo.getCustomAvatarUrl(), makerInfo.getCustomAvatarUrl());
+                    boolean tagsChanged = checkMarkerTagsChanged(cacheInfo,makerInfo);
 
-                    if (nameChanged || selectedChanged || avatarChanged) {
-                        LogUtil.i(TAG, "updateMarkers==修改头像");
+                    if (nameChanged || selectedChanged || avatarChanged || tagsChanged) {
+                        LogUtil.d(TAG, "updateMarkers==marker内容发生变化,更新样式");
                         ImageCacheLoader.loadBitmapAsync(context, makerInfo.getCustomAvatarUrl(), new BitmapCallback() {
                             @Override
                             public void onStartLoading() {
@@ -201,30 +204,40 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
                             public void onBitmapLoaded(Bitmap bitmap) {
                                 BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, bitmap);
                                 marker.setIcon(markerBitmap);
-                                LogUtil.i(TAG, "updateMarkers=成功修改头像:id=" + makerInfo.getId());
+                                LogUtil.d(TAG, "updateMarkers=url头像:id=" + makerInfo.getId());
                             }
 
                             @Override
                             public void onError(String errorMessage) {
-                                LogUtil.i(TAG, "updateMarkers==修改失败:" + errorMessage);
+                                LogUtil.d(TAG, "updateMarkers==url头像失败:" + errorMessage);
+                                BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, null);
+                                marker.setIcon(markerBitmap);
                             }
                         });
                     }
                     marker.setZIndex(makerInfo.isSelected() ? 100 : 0);
                 }
-                marker.setObject(makerInfo);
             }
+            marker.setObject(makerInfo);
         } else {
+            LogUtil.d(TAG, "updateMarkers==>marker不存在,创建新 marker:" + makerInfo);
             // 创建新 marker
             loadAvatarAndCreateMarker(makerInfo, latLng);
         }
     }
 
+    private boolean checkMarkerTagsChanged(MakerInfo cacheInfo, MakerInfo makerInfo) {
+        if(makerInfo.getMarkerType() == MakerInfo.MarkerType.FRIEND){
+            if(!Objects.equals(cacheInfo.getTags(), makerInfo.getTags())){
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void loadAvatarAndCreateMarker(final MakerInfo makerInfo, final LatLng latLng) {
-        if (TextUtils.isEmpty(makerInfo.getCustomAvatarUrl())) {
-            // 无头像,使用默认图标
-            addMarkerToMap(makerInfo, latLng, null);
-        } else {
+        Marker marker = addMarkerToMap(makerInfo, latLng, null);
+        if (!TextUtils.isEmpty(makerInfo.getCustomAvatarUrl())) {
             // 加载头像后再添加 marker
             ImageCacheLoader.loadBitmapAsync(context, makerInfo.getCustomAvatarUrl(), new BitmapCallback() {
                 @Override
@@ -233,21 +246,20 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
 
                 @Override
                 public void onBitmapLoaded(Bitmap bitmap) {
-                    addMarkerToMap(makerInfo, latLng, bitmap);
-                    LogUtil.i(TAG, "updateMarkers=成功修改头像:id=" + makerInfo.getId());
+                    BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, bitmap);
+                    marker.setIcon(markerBitmap);
                 }
 
                 @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) {
+    private Marker addMarkerToMap(MakerInfo makerInfo, LatLng latLng, Bitmap avatarBitmap) {
         BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, avatarBitmap);
         MarkerOptions markerOption = new MarkerOptions()
                 .position(latLng)
@@ -264,6 +276,7 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
         newMarker.setObject(makerInfo);
         newMarker.setZIndex(makerInfo.isSelected() ? 999 : 0);
         currentMarkers.put(makerInfo.getId(), newMarker);
+        return newMarker;
     }
 
 
@@ -273,25 +286,62 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
     }
 
 
+    @SuppressLint("SetTextI18n")
     public BitmapDescriptor getMarkerBitmap(MakerInfo markerInfo, Bitmap customAvatarBitmap) {
-        if (markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE || markerInfo.getMarkerType() == MakerInfo.MarkerType.FRIEND) {
+        if (markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE) {
             if (markerBinding == null) {
                 markerBinding = ItemLocationMarkerBinding.inflate(LayoutInflater.from(context));
             }
             ConstraintLayout view = markerBinding.getRoot();
             markerBinding.locationMarkerTvName.setText(markerInfo.getMarkerName());
             if (customAvatarBitmap == null) {
-                markerBinding.ivMarkerAvatar.setImageResource(markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE ? R.drawable.icon_default_mine_avatar : R.drawable.icon_default_friend_avatar);
+                markerBinding.ivMarkerAvatar.setImageResource(R.drawable.icon_default_mine_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_mine_marker_selected_bottom_circle : R.drawable.icon_mine_marker_normal_bottom_circle);
+            markerBinding.locationMarkerIvBg.setImageResource(markerInfo.isSelected() ? R.drawable.icon_location_mine_marker_selected : R.drawable.icon_location_marker_normal);
+
+            return viewGetBitmapDescriptor(view);
+        } else if (markerInfo.getMarkerType() == MakerInfo.MarkerType.FRIEND) {
+            Object tags = markerInfo.getTags();
+            Integer electric = null;
+            if (tags instanceof Map) {
+                Map<String, Object> tagsMap = (Map<String, Object>) tags;
+                electric = ParamUtil.getInt(tagsMap, "electric");
+            }
+            if (!markerInfo.isSelected() || electric == null) {
+                if (markerBinding == null) {
+                    markerBinding = ItemLocationMarkerBinding.inflate(LayoutInflater.from(context));
+                }
+                ConstraintLayout view = markerBinding.getRoot();
+                markerBinding.locationMarkerTvName.setText(markerInfo.getMarkerName());
+                if (customAvatarBitmap == null) {
+                    markerBinding.ivMarkerAvatar.setImageResource(R.drawable.icon_default_friend_avatar);
+                } else {
+                    markerBinding.ivMarkerAvatar.setImageBitmap(customAvatarBitmap);
+                }
                 markerBinding.vBottom.setBackgroundResource(markerInfo.isSelected() ? R.drawable.icon_marker_selected_bottom_circle : R.drawable.icon_marker_normal_bottom_circle);
+                markerBinding.locationMarkerIvBg.setImageResource(markerInfo.isSelected() ? R.drawable.icon_location_marker_selected : R.drawable.icon_location_marker_normal);
+
+                return viewGetBitmapDescriptor(view);
+            } else {
+                if (electricMarkerBinding == null) {
+                    electricMarkerBinding = ItemElectricMarkerBinding.inflate(LayoutInflater.from(context));
+                }
+                ConstraintLayout view = electricMarkerBinding.getRoot();
+                electricMarkerBinding.batteryView.setBatteryLevel(electric);
+                electricMarkerBinding.tvElectric.setText(electric + "%");
+                if (customAvatarBitmap == null) {
+                    electricMarkerBinding.ivMarkerAvatar.setImageResource(R.drawable.icon_default_friend_avatar);
+                } else {
+                    electricMarkerBinding.ivMarkerAvatar.setImageBitmap(customAvatarBitmap);
+                }
+                electricMarkerBinding.vBottom.setBackgroundResource(markerInfo.isSelected() ? R.drawable.icon_marker_selected_bottom_circle : R.drawable.icon_marker_normal_bottom_circle);
+                electricMarkerBinding.locationMarkerIvBg.setImageResource(markerInfo.isSelected() ? R.drawable.icon_location_marker_selected : R.drawable.icon_location_marker_normal);
+
+                return viewGetBitmapDescriptor(view);
             }
-            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) {
             ItemTrackStartMarkerBinding trackStartPointMarkerBinding = ItemTrackStartMarkerBinding.inflate(LayoutInflater.from(context));
             ConstraintLayout view = trackStartPointMarkerBinding.getRoot();

+ 3 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/ParamUtil.java

@@ -28,6 +28,9 @@ public class ParamUtil {
         if (value == null) {
             return null;
         }
+        if (value instanceof Double) {
+            return ((Double) value).intValue();
+        }
         return (int) value;
     }
 

+ 118 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/widget/BatteryView.java

@@ -0,0 +1,118 @@
+package com.atmob.map_amap_android.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class BatteryView extends View {
+
+    private int level = 100;
+
+    private Paint borderPaint;
+    private Paint fillPaint;
+    private Paint headPaint;
+
+    private int borderColor = Color.parseColor("#4476FF");
+    private int fillColor = Color.parseColor("#4476FF");
+    private int lowColor = Color.parseColor("#4476FF");
+    private int normalColor = Color.parseColor("#4476FF");
+
+    private RectF bodyRect = new RectF();
+    private RectF headRect = new RectF();
+    private RectF fillRect = new RectF();
+
+    private float borderStrokeWidth = 6f;
+    private float headStrokeWidth = 6f;
+    private float cornerRadius = 3f;
+    private float headGap = 6f;
+
+    public BatteryView(Context context) {
+        super(context);
+        init();
+    }
+
+    public BatteryView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        float headWidth = headStrokeWidth;
+        float headHeight = h / 2f;
+        float gap = headGap;
+
+        float bodyRight = w - headWidth - gap - borderStrokeWidth;
+        bodyRect.set(
+                borderStrokeWidth / 2f,
+                borderStrokeWidth / 2f,
+                bodyRight,
+                h - borderStrokeWidth / 2f
+        );
+
+        float headLeft = bodyRight + gap;
+        float headTop = (h - headHeight) / 2f;
+        headRect.set(headLeft, headTop, headLeft + headWidth, headTop + headHeight);
+    }
+
+    private void init() {
+        borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        borderPaint.setColor(borderColor);
+        borderPaint.setStyle(Paint.Style.STROKE);
+        borderPaint.setStrokeWidth(borderStrokeWidth);
+
+        fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        fillPaint.setColor(fillColor);
+
+        headPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        headPaint.setColor(borderColor);
+        headPaint.setStyle(Paint.Style.FILL);
+    }
+
+    public void setBatteryLevel(int level) {
+        this.level = Math.max(0, Math.min(100, level));
+        invalidate();
+    }
+
+    public void setBorderStrokeWidth(float strokeWidth) {
+        this.borderStrokeWidth = strokeWidth;
+        borderPaint.setStrokeWidth(strokeWidth);
+        invalidate();
+    }
+
+    public void setHeadStrokeWidth(float strokeWidth) {
+        this.headStrokeWidth = strokeWidth;
+        invalidate();
+    }
+
+    public void setCornerRadius(float radius) {
+        this.cornerRadius = radius;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        canvas.drawRoundRect(bodyRect, cornerRadius, cornerRadius, borderPaint);
+        canvas.drawRect(headRect, headPaint);
+
+        fillPaint.setColor(level <= 20 ? lowColor : normalColor);
+
+        float fillPadding = borderStrokeWidth;
+        float fillWidth = (bodyRect.width() - fillPadding * 2) * level / 100f;
+        fillRect.set(
+                bodyRect.left + fillPadding,
+                bodyRect.top + fillPadding,
+                bodyRect.left + fillPadding + fillWidth,
+                bodyRect.bottom - fillPadding
+        );
+        canvas.drawRoundRect(fillRect, cornerRadius / 1.5f, cornerRadius / 1.5f, fillPaint);
+    }
+}

+ 5 - 0
plugins/map_amap_android/android/src/main/res/drawable/bg_friend_electric.xml

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

+ 82 - 0
plugins/map_amap_android/android/src/main/res/layout/item_electric_marker.xml

@@ -0,0 +1,82 @@
+<?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">
+
+
+    <LinearLayout
+        android:id="@+id/ll_electric"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="2dp"
+        android:background="@drawable/bg_friend_electric"
+        android:elevation="2dp"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        android:paddingHorizontal="9dp"
+        android:paddingVertical="3dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <com.atmob.map_amap_android.widget.BatteryView
+            android:id="@+id/battery_view"
+            android:layout_width="18dp"
+            android:layout_height="9.8dp" />
+
+        <TextView
+            android:id="@+id/tv_electric"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="2dp"
+            android:textColor="#666666"
+            android:textSize="11dp"
+            tools:text="40%" />
+
+    </LinearLayout>
+
+    <Space
+        android:id="@+id/space_bottom"
+        android:layout_width="wrap_content"
+        android:layout_height="44dp"
+        app:layout_constraintEnd_toEndOf="@+id/location_marker_iv_bg"
+        app:layout_constraintStart_toStartOf="@+id/location_marker_iv_bg"
+        app:layout_constraintTop_toTopOf="@+id/location_marker_iv_bg" />
+
+
+    <View
+        android:id="@+id/v_bottom"
+        android:layout_width="11dp"
+        android:layout_height="11dp"
+        app:layout_constraintEnd_toEndOf="@+id/location_marker_iv_bg"
+        app:layout_constraintStart_toStartOf="@+id/location_marker_iv_bg"
+        app:layout_constraintTop_toBottomOf="@+id/space_bottom"
+        tools:background="@drawable/icon_marker_selected_bottom_circle" />
+
+    <ImageView
+        android:id="@+id/location_marker_iv_bg"
+        android:layout_width="44dp"
+        android:layout_height="0dp"
+        android:layout_marginTop="6dp"
+        app:layout_constraintDimensionRatio="132:150"
+        app:layout_constraintEnd_toEndOf="@+id/ll_electric"
+        app:layout_constraintStart_toStartOf="@+id/ll_electric"
+        app:layout_constraintTop_toBottomOf="@+id/ll_electric"
+        tools:src="@drawable/icon_location_marker_selected" />
+
+
+    <ImageView
+        android:id="@+id/iv_marker_avatar"
+        android:layout_width="38dp"
+        android:layout_height="38dp"
+        app:layout_constraintBottom_toBottomOf="@+id/location_marker_iv_bg"
+        app:layout_constraintEnd_toEndOf="@+id/location_marker_iv_bg"
+        app:layout_constraintStart_toStartOf="@+id/location_marker_iv_bg"
+        app:layout_constraintTop_toTopOf="@+id/location_marker_iv_bg"
+        app:layout_constraintVertical_bias="0.25"
+        tools:src="@drawable/icon_default_friend_avatar" />
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>