1.处理轨迹经纬度飘和圆滑度

2.增加围栏缩放最小值提示
3.增加map和直播显示多宠物设置
This commit is contained in:
yezhiqiu
2026-05-28 11:01:51 +08:00
parent f26e3545b2
commit fde637793e
27 changed files with 557 additions and 77 deletions

View File

@@ -57,6 +57,11 @@ abstract class BaseDialog<T : ViewBinding>
initView() initView()
} }
override fun onStart() {
super.onStart()
// ✅ 每次显示都会执行刷新数据、更新UI等
}
abstract fun initView() abstract fun initView()
/** /**

View File

@@ -36,12 +36,14 @@ data class PetBean(
var showDFUDialog: Boolean,//是否显示了固件升级弹窗 var showDFUDialog: Boolean,//是否显示了固件升级弹窗
var showNoWifiDialog: Boolean,//是否显示了没有设置wifi弹窗 var showNoWifiDialog: Boolean,//是否显示了没有设置wifi弹窗
var availableOrder: Int,//判断套餐是否可用或过期1是可用 0不可用 var availableOrder: Int,//判断套餐是否可用或过期1是可用 0不可用
var latitude: Double,
var longitude: Double,
var deviceId: String var deviceId: String
) : Parcelable, BaseDiffBean() { ) : Parcelable, BaseDiffBean() {
constructor() : this( constructor() : this(
"", "", "", "", 0, 1, MMKVUtil.getString(MMKVKey.UserId), "", 0, Utils.formatTime( "", "", "", "", 0, 1, MMKVUtil.getString(MMKVKey.UserId), "", 0, Utils.formatTime(
System.currentTimeMillis(), Utils.DATE_FORMAT_PATTERN_CN System.currentTimeMillis(), Utils.DATE_FORMAT_PATTERN_CN
), "", 0f, 0f, "", "", "", 1, 1, "", false, false, 1, "" ), "", 0f, 0f, "", "", "", 1, 1, "", false, false, 1, 0.0, 0.0, ""
) )
override fun isSameObject(other: Any): Boolean { override fun isSameObject(other: Any): Boolean {

View File

@@ -55,6 +55,7 @@ import androidx.annotation.StringDef
ConstantString.Upgrade, ConstantString.Upgrade,
ConstantString.Password, ConstantString.Password,
ConstantString.PackageUnitDay, ConstantString.PackageUnitDay,
ConstantString.Lists,
ConstantString.PackageUnitMonth, ConstantString.PackageUnitMonth,
ConstantString.PackageUnitYear, ConstantString.PackageUnitYear,
ConstantString.SetMealIndex ConstantString.SetMealIndex
@@ -95,6 +96,7 @@ annotation class ConstantString {
const val Latitude = "latitude" const val Latitude = "latitude"
const val Longitude = "longitude" const val Longitude = "longitude"
const val isFirstBind = "isFirstBind" const val isFirstBind = "isFirstBind"
const val Lists = "list"
const val isCheck = "isCheck" const val isCheck = "isCheck"
const val Amount = "amount" const val Amount = "amount"

View File

@@ -490,6 +490,10 @@ class FencesAddActivity : BaseActivity<ActivityFencesAddBinding>(ActivityFencesA
override fun rectStartDragClick() { override fun rectStartDragClick() {
} }
override fun rectMinSizeClick() {
}
override fun rectEndDragClick() { override fun rectEndDragClick() {
} }
@@ -504,6 +508,9 @@ class FencesAddActivity : BaseActivity<ActivityFencesAddBinding>(ActivityFencesA
} }
override fun circleMinSizeClick() {
}
override fun circleEndDragClick() { override fun circleEndDragClick() {
} }

View File

@@ -1,6 +1,7 @@
package com.abbidot.tracker.dialog package com.abbidot.tracker.dialog
import android.content.Context import android.content.Context
import android.view.View
import android.widget.CompoundButton import android.widget.CompoundButton
import com.abbidot.baselibrary.constant.MMKVKey import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.list.BaseRecyclerAdapter import com.abbidot.baselibrary.list.BaseRecyclerAdapter
@@ -20,14 +21,16 @@ import com.abbidot.tracker.util.ViewUtil
class SelectMapTypeDialog( class SelectMapTypeDialog(
context: Context, context: Context,
adapter: BaseRecyclerAdapter<*>, adapter: BaseRecyclerAdapter<*>,
fenceCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null, fenceCheckedListener: CompoundButton.OnCheckedChangeListener? = null,
dashedCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null dashedCheckedListener: CompoundButton.OnCheckedChangeListener? = null,
showAllPetCheckedListener: CompoundButton.OnCheckedChangeListener? = null
) : BaseDialog<DialogSelectMapTypeLayoutBinding>( ) : BaseDialog<DialogSelectMapTypeLayoutBinding>(
DialogSelectMapTypeLayoutBinding::inflate, context DialogSelectMapTypeLayoutBinding::inflate, context
) { ) {
private var mAdapter = adapter private var mAdapter = adapter
private val mFenceCheckedChangeListener = fenceCheckedChangeListener private val mFenceCheckedListener = fenceCheckedListener
private val mDashedCheckedChangeListener = dashedCheckedChangeListener private val mDashedCheckedListener = dashedCheckedListener
private val mShowAllPetCheckedListener = showAllPetCheckedListener
override fun initView() { override fun initView() {
mViewBinding.apply { mViewBinding.apply {
@@ -35,13 +38,15 @@ class SelectMapTypeDialog(
context, rvShowMapTypeList, mAdapter, right = AppUtils.dpToPx(58) context, rvShowMapTypeList, mAdapter, right = AppUtils.dpToPx(58)
) )
cbDialogMapFencesSwitch.isChecked = Util.getShowFenceSp() cbDialogMapFencesSwitch.isChecked = Util.getShowFenceSp()
cbDialogMapDashedLineSwitch.isChecked = cbDialogMapDashedLineSwitch.isChecked = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine)
MMKVUtil.getBoolean(MMKVKey.ShowDashedLine, false) cbDialogMapAllPetSwitch.isChecked = MMKVUtil.getBoolean(MMKVKey.ShowAllPet)
cbDialogMapFencesSwitch.setOnCheckedChangeListener(mFenceCheckedChangeListener) cbDialogMapFencesSwitch.setOnCheckedChangeListener(mFenceCheckedListener)
cbDialogMapDashedLineSwitch.setOnCheckedChangeListener(mDashedCheckedChangeListener) cbDialogMapDashedLineSwitch.setOnCheckedChangeListener(mDashedCheckedListener)
cbDialogMapAllPetSwitch.setOnCheckedChangeListener(mShowAllPetCheckedListener)
} }
} }
/** /**
* 检测地图类型有没有改变 * 检测地图类型有没有改变
*/ */
@@ -69,4 +74,12 @@ class SelectMapTypeDialog(
fun setDashedSwitch(checked: Boolean) { fun setDashedSwitch(checked: Boolean) {
mViewBinding.cbDialogMapDashedLineSwitch.isChecked = checked mViewBinding.cbDialogMapDashedLineSwitch.isChecked = checked
} }
fun setShowAllPetSwitch(checked: Boolean) {
mViewBinding.cbDialogMapAllPetSwitch.isChecked = checked
}
fun showAllPetSwitch(isShow: Boolean) {
mViewBinding.llMapTypeShowAllPetSwitch.visibility = if (isShow) View.VISIBLE else View.GONE
}
} }

View File

@@ -8,6 +8,7 @@ import android.net.Network
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.NetworkRequest import android.net.NetworkRequest
import android.text.TextUtils import android.text.TextUtils
import android.view.Gravity
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.activity.viewModels import androidx.activity.viewModels
@@ -92,7 +93,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
var mSelectPetPosition = 0 var mSelectPetPosition = 0
//宠物数据 //宠物数据
lateinit var mPetList: MutableList<PetBean> val mPetList = mutableListOf<PetBean>()
private var isFirst = true private var isFirst = true
//是否请求过宠物数据 //是否请求过宠物数据
@@ -163,7 +164,6 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
it.itemIconTintList = null it.itemIconTintList = null
it.setOnItemSelectedListener(this) it.setOnItemSelectedListener(this)
} }
mPetList = mutableListOf()
mChangePetListDialogAdapter = ChangePetListDialogAdapter(mContext, mPetList).apply { mChangePetListDialogAdapter = ChangePetListDialogAdapter(mContext, mPetList).apply {
setOnChangeClickListener(this@HomeV2Activity) setOnChangeClickListener(this@HomeV2Activity)
} }
@@ -175,7 +175,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
val isCrash = MMKVUtil.getBoolean(MMKVKey.isCrash, false) val isCrash = MMKVUtil.getBoolean(MMKVKey.isCrash, false)
if (isCrash) { if (isCrash) {
MMKVUtil.putBoolean(MMKVKey.isCrash, false) MMKVUtil.putBoolean(MMKVKey.isCrash, false)
showToast(R.string.txt_show_crash) showToast(R.string.txt_show_crash, gravity = Gravity.CENTER)
mCountdownType = ConstantInt.Type0 mCountdownType = ConstantInt.Type0
mCountDownTimerViewModel.startCountDown(4) mCountDownTimerViewModel.startCountDown(4)
} else { } else {

View File

@@ -838,6 +838,11 @@ open class AddAndEditFencesZoneBaseActivity :
mFencesBean.startDrag = true mFencesBean.startDrag = true
} }
override fun rectMinSizeClick() {
if (isLimitClick()) return
showToast(R.string.txt_already_min_size, gravity = Gravity.CENTER)
}
override fun rectEndDragClick() { override fun rectEndDragClick() {
mFencesBean.startDrag = false mFencesBean.startDrag = false
mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0) mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0)
@@ -855,6 +860,11 @@ open class AddAndEditFencesZoneBaseActivity :
mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0) mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0)
} }
override fun circleMinSizeClick() {
if (isLimitClick()) return
showToast(R.string.txt_already_min_size, gravity = Gravity.CENTER)
}
override fun circleEndDragClick() { override fun circleEndDragClick() {
mFencesBean.startDrag = false mFencesBean.startDrag = false
mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0) mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0)
@@ -868,6 +878,11 @@ open class AddAndEditFencesZoneBaseActivity :
mFencesBean.startDrag = true mFencesBean.startDrag = true
} }
override fun polygonMinSizeClick() {
if (isLimitClick()) return
showToast(R.string.txt_already_min_size, gravity = Gravity.CENTER)
}
override fun polygonEndDragClick() { override fun polygonEndDragClick() {
mFencesBean.startDrag = false mFencesBean.startDrag = false
mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0) mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0)

View File

@@ -94,6 +94,9 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
//是否显示虚线 //是否显示虚线
private var isShowDashed = true private var isShowDashed = true
//是否显示所有宠物
private var isShowAllPet = false
//地图类型,标准和卫星地图 //地图类型,标准和卫星地图
private var mMapType = ConstantInt.Type0 private var mMapType = ConstantInt.Type0
private var mShowCenterLocation = ConstantInt.PetLocationType private var mShowCenterLocation = ConstantInt.PetLocationType
@@ -102,6 +105,7 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
private var isLiveJump = true private var isLiveJump = true
private var mLiveStatus = 1 private var mLiveStatus = 1
private var mPetBean: PetBean? = null private var mPetBean: PetBean? = null
private var mPetList: MutableList<PetBean>? = null
private var mTrackBleDevice: BleTrackDeviceBean? = null private var mTrackBleDevice: BleTrackDeviceBean? = null
//直播是否开始 //直播是否开始
@@ -138,10 +142,14 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
isCloseMsg = getBoolean(ConstantString.PetAuthorized) isCloseMsg = getBoolean(ConstantString.PetAuthorized)
mMapViewModel.mDeviceMsgType = getInt(ConstantString.Type, ConstantInt.SpecialType) mMapViewModel.mDeviceMsgType = getInt(ConstantString.Type, ConstantInt.SpecialType)
mPetBean = Util.getParcelableAdaptive(intent, ConstantString.Pet, PetBean::class.java) mPetBean = Util.getParcelableAdaptive(intent, ConstantString.Pet, PetBean::class.java)
mPetList = Util.getParcelableArrayListAdaptive(
intent, ConstantString.Lists, PetBean::class.java
)
} }
isShowFence = Util.getShowFenceSp() isShowFence = Util.getShowFenceSp()
isShowDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine, false) isShowDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine)
isShowAllPet = MMKVUtil.getBoolean(MMKVKey.ShowAllPet)
mViewBinding.apply { mViewBinding.apply {
ViewUtil.instance.viewRotationAnimator( ViewUtil.instance.viewRotationAnimator(
@@ -314,6 +322,10 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
mDeviceStateList, mDeviceStateList,
mDeviceStateAdapter mDeviceStateAdapter
) )
if (MMKVUtil.getBoolean(MMKVKey.ShowAllPet)) {
setAllPetShow(true)
}
} }
} }
@@ -808,13 +820,43 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
} else { } else {
mHomeMapCommon.removeUserAndPetLine() mHomeMapCommon.removeUserAndPetLine()
} }
}) },
{ _, isChecked ->
setAllPetShow(isChecked)
}).apply {
setOnShowListener {
mPetList?.let {
if (it.size > 1) mSelectMapTypeDialog?.showAllPetSwitch(true)
else mSelectMapTypeDialog?.showAllPetSwitch(false)
}
}
}
} else { } else {
mSelectMapTypeDialog!!.mapTypeSpToUpdate() mSelectMapTypeDialog!!.mapTypeSpToUpdate()
} }
mSelectMapTypeDialog?.show() mSelectMapTypeDialog?.show()
} }
/**
* 设置多宠物显示
*/
private fun setAllPetShow(isShow: Boolean) {
isShowAllPet = isShow
MMKVUtil.putBoolean(MMKVKey.ShowAllPet, isShow)
if (isShow) {
lifecycleScope.launch {
mPetList?.apply {
mPetBean?.let { p ->
//删除当前选中宠物
mHomeMapCommon.showOtherPetHeadMarker(filter { it != p }.toMutableList())
}
}
}
} else {
mHomeMapCommon.clearOtherPetHeadMarker()
}
}
/** /**
* 设置地图相关数据 * 设置地图相关数据
*/ */
@@ -928,7 +970,7 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
it.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0) it.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
} }
mViewBinding.llLiveV2MapTopPet.homeDataPetHeadSmall.appHeadImage.let { mViewBinding.llLiveV2MapTopPet.homeDataPetHeadSmall.appHeadImage.let {
ViewUtil.instance.setPetTypeHead(mContext,it, imgurl, petType) ViewUtil.instance.setPetTypeHead(mContext, it, imgurl, petType)
ViewUtil.instance.viewAlphaAndRotationObjectAnimator(it) ViewUtil.instance.viewAlphaAndRotationObjectAnimator(it)
} }
//重新设置地图宠物头像 //重新设置地图宠物头像

View File

@@ -6,9 +6,11 @@ import androidx.fragment.app.Fragment
import com.abbidot.baselibrary.constant.MMKVKey import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.AppUtils import com.abbidot.baselibrary.util.AppUtils
import com.abbidot.baselibrary.util.MMKVUtil import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.tracker.R
import com.abbidot.tracker.base.BaseMapCommon import com.abbidot.tracker.base.BaseMapCommon
import com.abbidot.tracker.bean.HistoryDataBean import com.abbidot.tracker.bean.HistoryDataBean
import com.abbidot.tracker.bean.MapDeviceBean import com.abbidot.tracker.bean.MapDeviceBean
import com.abbidot.tracker.bean.PetBean
import com.abbidot.tracker.constant.ConstantInt import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.databinding.LayoutPetLocationInfoBinding import com.abbidot.tracker.databinding.LayoutPetLocationInfoBinding
import com.abbidot.tracker.ui.fragment.map.baidumap.HomeMapBaiduMapFragment import com.abbidot.tracker.ui.fragment.map.baidumap.HomeMapBaiduMapFragment
@@ -206,6 +208,18 @@ class HomeMapCommonV3 @Inject constructor() : BaseMapCommon() {
mHomeMapGoogleMapFragment?.removeUserAndPetLine() mHomeMapGoogleMapFragment?.removeUserAndPetLine()
} }
suspend fun showOtherPetHeadMarker(
petList: MutableList<PetBean>, headBgResId: Int = R.drawable.pic_map_gps_avatar
) {
if (petList.size > 0) {
mHomeMapGoogleMapFragment?.showOtherPetHeadMarker(petList, headBgResId)
}
}
fun clearOtherPetHeadMarker() {
mHomeMapGoogleMapFragment?.clearOtherPetHeadMarker()
}
// fun getAddressShowMarkerInfoWindow(historyDataBean: HistoryDataBean) { // fun getAddressShowMarkerInfoWindow(historyDataBean: HistoryDataBean) {
// if (null != mHomeMapBaiduMapFragment) { // if (null != mHomeMapBaiduMapFragment) {
// mHomeMapBaiduMapFragment!!.showMarkerInfoWindow(historyDataBean) // mHomeMapBaiduMapFragment!!.showMarkerInfoWindow(historyDataBean)

View File

@@ -17,6 +17,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.commit import androidx.fragment.app.commit
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.abbidot.baselibrary.constant.ConState import com.abbidot.baselibrary.constant.ConState
import com.abbidot.baselibrary.constant.EventName import com.abbidot.baselibrary.constant.EventName
import com.abbidot.baselibrary.constant.MMKVKey import com.abbidot.baselibrary.constant.MMKVKey
@@ -61,6 +62,7 @@ import com.google.android.gms.maps.model.LatLng
import com.hjq.permissions.XXPermissions import com.hjq.permissions.XXPermissions
import com.hjq.permissions.permission.PermissionLists import com.hjq.permissions.permission.PermissionLists
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -91,8 +93,6 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
private lateinit var mDeviceStateList: MutableList<DataBean> private lateinit var mDeviceStateList: MutableList<DataBean>
private lateinit var mDeviceStateAdapter: HomeMapDeviceStateAdapter private lateinit var mDeviceStateAdapter: HomeMapDeviceStateAdapter
// private lateinit var mDeviceMsgList: MutableList<DataBean>
// private lateinit var mDeviceMsgAdapter: HomeMapDeviceMsgAdapter
//启动移动地图摄像机 //启动移动地图摄像机
private var isMoveCamera = true private var isMoveCamera = true
@@ -102,7 +102,10 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
private var isShowFence = true private var isShowFence = true
//是否显示虚线 //是否显示虚线
private var isShowDashed = true private var isShowDashed = false
//是否显示所有宠物
private var isShowAllPet = false
//地图类型,标准和卫星地图 //地图类型,标准和卫星地图
private var mMapType = ConstantInt.Type0 private var mMapType = ConstantInt.Type0
@@ -112,6 +115,8 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
//是否需要gps坐标转火星坐标 //是否需要gps坐标转火星坐标
private var needGpsToGCJ02 = true private var needGpsToGCJ02 = true
//一键定位绿波动画
private var mAnimatorSet: AnimatorSet? = null private var mAnimatorSet: AnimatorSet? = null
//一键定位开始的时间戳 //一键定位开始的时间戳
@@ -131,7 +136,8 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
} }
isShowFence = Util.getShowFenceSp() isShowFence = Util.getShowFenceSp()
isShowDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine, false) isShowDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine)
isShowAllPet = MMKVUtil.getBoolean(MMKVKey.ShowAllPet)
mViewBinding.apply { mViewBinding.apply {
getHomeV2Activity()?.edgeToEdgeAdapterBars( getHomeV2Activity()?.edgeToEdgeAdapterBars(
@@ -220,7 +226,8 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
} }
val showFence = Util.getShowFenceSp() val showFence = Util.getShowFenceSp()
val showDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine, false) val showDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine)
val showAllPet = MMKVUtil.getBoolean(MMKVKey.ShowAllPet)
//检测直播页面有没有修改围栏显示 //检测直播页面有没有修改围栏显示
if (isShowFence != showFence) { if (isShowFence != showFence) {
if (null == mSelectMapTypeDialog) { if (null == mSelectMapTypeDialog) {
@@ -236,6 +243,13 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
mSelectMapTypeDialog?.setDashedSwitch(showDashed) mSelectMapTypeDialog?.setDashedSwitch(showDashed)
} }
} }
if (isShowAllPet != showAllPet) {
if (null == mSelectMapTypeDialog) {
setAllPetShow(showAllPet)
} else {
mSelectMapTypeDialog?.setShowAllPetSwitch(showDashed)
}
}
} }
private fun getHomeV2Activity(): HomeV2Activity? { private fun getHomeV2Activity(): HomeV2Activity? {
@@ -600,7 +614,8 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
* 显示地图类型选择弹窗 * 显示地图类型选择弹窗
*/ */
private fun showMapTypeDialog() { private fun showMapTypeDialog() {
if (null == mSelectMapTypeDialog) mSelectMapTypeDialog = ViewUtil.instance.getMapTypeDialog( if (null == mSelectMapTypeDialog) {
mSelectMapTypeDialog = ViewUtil.instance.getMapTypeDialog(
mContext!!, object : BaseRecyclerAdapter.OnItemClickListener { mContext!!, object : BaseRecyclerAdapter.OnItemClickListener {
override fun onItemClick(itemView: View?, pos: Int) { override fun onItemClick(itemView: View?, pos: Int) {
mMapType = Util.getMapTypeSp() mMapType = Util.getMapTypeSp()
@@ -609,11 +624,19 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
} }
mHomeMapCommon.switchSatelliteAndNormalMapType() mHomeMapCommon.switchSatelliteAndNormalMapType()
} }
}, this, this }, this, this, this
) ).apply {
else { setOnShowListener {
getHomeV2Activity()?.apply {
if (mPetList.size > 1) mSelectMapTypeDialog?.showAllPetSwitch(true)
else mSelectMapTypeDialog?.showAllPetSwitch(false)
}
}
}
} else {
mSelectMapTypeDialog!!.mapTypeSpToUpdate() mSelectMapTypeDialog!!.mapTypeSpToUpdate()
} }
mSelectMapTypeDialog?.show() mSelectMapTypeDialog?.show()
} }
@@ -645,6 +668,27 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
} }
} }
/**
* 设置多宠物显示
*/
private fun setAllPetShow(isShow: Boolean) {
isShowAllPet = isShow
MMKVUtil.putBoolean(MMKVKey.ShowAllPet, isShow)
if (isShow) {
lifecycleScope.launch {
getHomeV2Activity()?.apply {
getPet(false)?.let { p ->
//删除当前选中宠物
mHomeMapCommon.showOtherPetHeadMarker(mPetList.filter { it != p }
.toMutableList())
}
}
}
} else {
mHomeMapCommon.clearOtherPetHeadMarker()
}
}
/** /**
* 设置需要更新的标识 * 设置需要更新的标识
*/ */
@@ -755,6 +799,9 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
if (isShowDashed) { if (isShowDashed) {
mHomeMapCommon.addUserAndPetLine() mHomeMapCommon.addUserAndPetLine()
} }
if (isShowAllPet) {
setAllPetShow(true)
}
} }
} }
@@ -951,6 +998,9 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
getPet()?.apply { getPet()?.apply {
val intent = Intent(mContext, LiveActivityV3::class.java) val intent = Intent(mContext, LiveActivityV3::class.java)
intent.putExtra(ConstantString.JumpActivity, isLiveJump) intent.putExtra(ConstantString.JumpActivity, isLiveJump)
intent.putParcelableArrayListExtra(
ConstantString.Lists, mPetList.toCollection(ArrayList())
)
intent.putExtra(ConstantString.Pet, this) intent.putExtra(ConstantString.Pet, this)
mMapDeviceBean?.let { mMapDeviceBean?.let {
intent.putExtra(ConstantString.DeviceAuthorized, it.isCloseBattery) intent.putExtra(ConstantString.DeviceAuthorized, it.isCloseBattery)
@@ -1056,6 +1106,7 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
when (v.id) { when (v.id) {
R.id.cb_dialog_map_fences_switch -> setFencesShow(isChecked) R.id.cb_dialog_map_fences_switch -> setFencesShow(isChecked)
R.id.cb_dialog_map_dashed_line_switch -> setDashedShow(isChecked) R.id.cb_dialog_map_dashed_line_switch -> setDashedShow(isChecked)
R.id.cb_dialog_map_all_pet_switch -> setAllPetShow(isChecked)
} }
} }

View File

@@ -25,6 +25,7 @@ import com.abbidot.tracker.databinding.FragmentBaseBaiduMapBinding
import com.abbidot.tracker.ui.fragment.map.googlemap.GoogleBitmapHelper import com.abbidot.tracker.ui.fragment.map.googlemap.GoogleBitmapHelper
import com.abbidot.tracker.util.ImageUtil import com.abbidot.tracker.util.ImageUtil
import com.abbidot.tracker.util.LonAndLatUtil import com.abbidot.tracker.util.LonAndLatUtil
import com.abbidot.tracker.util.Util.Companion.safeRecycle
import com.abbidot.tracker.widget.TypefaceTextView import com.abbidot.tracker.widget.TypefaceTextView
import com.baidu.location.BDAbstractLocationListener import com.baidu.location.BDAbstractLocationListener
import com.baidu.location.BDLocation import com.baidu.location.BDLocation
@@ -218,7 +219,7 @@ abstract class BaseBaiduMapFragment :
*/ */
fun setPetHeadIconUrl(petHeadUrl: String) { fun setPetHeadIconUrl(petHeadUrl: String) {
mPetHeadUrl = petHeadUrl mPetHeadUrl = petHeadUrl
mPetHeadIconBitmap?.recycle() mPetHeadIconBitmap.safeRecycle()
mPetHeadIconBitmap = null mPetHeadIconBitmap = null
} }
@@ -247,20 +248,21 @@ abstract class BaseBaiduMapFragment :
headBgResId: Int = R.drawable.pic_map_gps_avatar headBgResId: Int = R.drawable.pic_map_gps_avatar
) { ) {
val mapKey = if (TextUtils.isEmpty(mPetHeadUrl)) "pet_default_head" // val mapKey = if (TextUtils.isEmpty(mPetHeadUrl)) "pet_default_head"
else mPetHeadUrl.substring(mPetHeadUrl.lastIndexOf("/") + 1, mPetHeadUrl.length) // else mPetHeadUrl.substring(mPetHeadUrl.lastIndexOf("/") + 1, mPetHeadUrl.length)
var bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey] // var bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) { // if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
if (null == mPetHeadIconBitmap) { // if (null == mPetHeadIconBitmap) {
//设置头像 // //设置头像
mPetHeadIconBitmap = // mPetHeadIconBitmap =
GoogleBitmapHelper.headToBitmap(mContext!!, headBgResId, mPetHeadUrl, mPetType) // GoogleBitmapHelper.headToBitmap(mContext!!, headBgResId, mPetHeadUrl, mPetType)
} // }
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mPetHeadIconBitmap!!) // bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mPetHeadIconBitmap!!)
//保存在内存中,提高显示速度 // //保存在内存中,提高显示速度
MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor // MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor
} // }
val bitmapDescriptor =
getMarkerBitmapDescriptor(mContext!!, mPetHeadUrl, mPetType, headBgResId)
mMarker?.remove() mMarker?.remove()
val latLng = getBaiduMapLatLng(lat, lon, needConvertGCJ02) val latLng = getBaiduMapLatLng(lat, lon, needConvertGCJ02)
mPetLatLng = latLng mPetLatLng = latLng
@@ -272,6 +274,42 @@ abstract class BaseBaiduMapFragment :
mMarker = mBaiduMap!!.addOverlay(option) mMarker = mBaiduMap!!.addOverlay(option)
} }
private suspend fun getMarkerBitmapDescriptor(
context: Context,
headUrl: String,
petType: Int,
headBgResId: Int = R.drawable.pic_map_gps_avatar
): BitmapDescriptor? {
var bitmap: Bitmap? = null
var bitmapDescriptor: Any?
try {
val mapKey = if (TextUtils.isEmpty(headUrl)) {
"pet_default_head"
} else {
val idx = headUrl.lastIndexOf("/")
if (idx != -1) {
headUrl.substring(idx + 1)
} else {
headUrl
}
}
LogUtil.e("mapKey=$mapKey")
bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
bitmap = GoogleBitmapHelper.headToBitmap(context, headBgResId, headUrl, petType)
bitmap?.let {
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(it)
//保存在内存中,提高显示速度
MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor as BitmapDescriptor
}
}
} finally {
bitmap.safeRecycle()
}
// 把 return 写在 finally 外面
return bitmapDescriptor as? BitmapDescriptor
}
/** /**
* 地图添加图片 * 地图添加图片
*/ */
@@ -853,7 +891,8 @@ abstract class BaseBaiduMapFragment :
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
mSensorManager?.unregisterListener(mSensorEventListener) mSensorManager?.unregisterListener(mSensorEventListener)
mPetHeadIconBitmap?.recycle() mPetHeadIconBitmap.safeRecycle()
mPetHeadIconBitmap = null
stopLocation() stopLocation()
//在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理 //在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理
mMapView.onDestroy() mMapView.onDestroy()

View File

@@ -8,7 +8,6 @@ import android.graphics.Bitmap
import android.graphics.Point import android.graphics.Point
import android.location.Location import android.location.Location
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -19,7 +18,6 @@ import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.AppUtils import com.abbidot.baselibrary.util.AppUtils
import com.abbidot.baselibrary.util.LogUtil import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.baselibrary.util.MMKVUtil import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.tracker.MyApplication
import com.abbidot.tracker.R import com.abbidot.tracker.R
import com.abbidot.tracker.base.BaseFragment import com.abbidot.tracker.base.BaseFragment
import com.abbidot.tracker.bean.FencesBean import com.abbidot.tracker.bean.FencesBean
@@ -30,6 +28,7 @@ import com.abbidot.tracker.databinding.FragmentGoogleMapBinding
import com.abbidot.tracker.util.ImageUtil import com.abbidot.tracker.util.ImageUtil
import com.abbidot.tracker.util.LonAndLatUtil import com.abbidot.tracker.util.LonAndLatUtil
import com.abbidot.tracker.util.Util import com.abbidot.tracker.util.Util
import com.abbidot.tracker.util.Util.Companion.safeRecycle
import com.abbidot.tracker.util.ViewUtil import com.abbidot.tracker.util.ViewUtil
import com.abbidot.tracker.widget.MapDeviceNetView import com.abbidot.tracker.widget.MapDeviceNetView
import com.abbidot.tracker.widget.MapMarkerInfoView import com.abbidot.tracker.widget.MapMarkerInfoView
@@ -42,7 +41,6 @@ import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMapOptions import com.google.android.gms.maps.GoogleMapOptions
import com.google.android.gms.maps.MapView import com.google.android.gms.maps.MapView
import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.CameraPosition import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.Circle import com.google.android.gms.maps.model.Circle
@@ -65,6 +63,12 @@ import com.google.android.gms.tasks.CancellationTokenSource
import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.Task
import com.qmuiteam.qmui.util.QMUIDisplayHelper import com.qmuiteam.qmui.util.QMUIDisplayHelper
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.PI
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
/** /**
@@ -97,7 +101,7 @@ abstract class BaseGoogleMapFragment :
var mMarker: Marker? = null var mMarker: Marker? = null
//自定显示用户地点信息图层 //自定显示用户地点信息图层
var mUserMarker: Marker? = null private var mUserMarker: Marker? = null
//宠物大头针设置自定义头像 //宠物大头针设置自定义头像
private var mPetHeadIconBitmap: Bitmap? = null private var mPetHeadIconBitmap: Bitmap? = null
@@ -318,9 +322,13 @@ abstract class BaseGoogleMapFragment :
} }
ConstantInt.UserLocationType -> { ConstantInt.UserLocationType -> {
mPetLatLng?.let { if (null == mPetLatLng) {
mUserLatLng?.apply { mUserLatLng?.apply {
setLatLngZoom(mContext!!, 250, it, this) moveCameraLocation(this)
}
} else {
mUserLatLng?.apply {
setLatLngZoom(mContext!!, 250, mPetLatLng!!, this)
cameraUpdateMove() cameraUpdateMove()
} }
} }
@@ -368,7 +376,7 @@ abstract class BaseGoogleMapFragment :
fun setPetHeadIconUrl(petHeadUrl: String, petType: Int) { fun setPetHeadIconUrl(petHeadUrl: String, petType: Int) {
mPetHeadUrl = petHeadUrl mPetHeadUrl = petHeadUrl
mPetType = petType mPetType = petType
mPetHeadIconBitmap?.recycle() mPetHeadIconBitmap.safeRecycle()
mPetHeadIconBitmap = null mPetHeadIconBitmap = null
} }
@@ -390,21 +398,26 @@ abstract class BaseGoogleMapFragment :
private suspend fun setPetHeadMarker( private suspend fun setPetHeadMarker(
latLng: LatLng, headBgResId: Int = R.drawable.pic_map_gps_avatar latLng: LatLng, headBgResId: Int = R.drawable.pic_map_gps_avatar
) { ) {
val mapKey = if (TextUtils.isEmpty(mPetHeadUrl)) "pet_default_head" // val mapKey = if (TextUtils.isEmpty(mPetHeadUrl)) "pet_default_head"
else mPetHeadUrl.substring(mPetHeadUrl.lastIndexOf("/") + 1, mPetHeadUrl.length) // else mPetHeadUrl.substring(mPetHeadUrl.lastIndexOf("/") + 1, mPetHeadUrl.length)
var bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey] // var bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) { // if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
if (null == mPetHeadIconBitmap) { // if (null == mPetHeadIconBitmap) {
//设置头像 // //设置头像
mPetHeadIconBitmap = // mPetHeadIconBitmap =
GoogleBitmapHelper.headToBitmap(mContext!!, headBgResId, mPetHeadUrl, mPetType) // GoogleBitmapHelper.headToBitmap(mContext!!, headBgResId, mPetHeadUrl, mPetType)
} // }
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mPetHeadIconBitmap!!) // bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mPetHeadIconBitmap!!)
//保存在内存中,提高显示速度 // //保存在内存中,提高显示速度
MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor // MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor
} // }
val bitmapDescriptor = GoogleBitmapHelper.getMarkerBitmapDescriptor(
mContext!!, mPetHeadUrl, mPetType, headBgResId
)
val newLatLng = toGCJ02LatLon(latLng) val newLatLng = toGCJ02LatLon(latLng)
val markerOptions = MarkerOptions().position(newLatLng) //显示最上面,层级最高,默认值 0f
val markerOptions = MarkerOptions().position(newLatLng).zIndex(99f)
markerOptions.icon(bitmapDescriptor) markerOptions.icon(bitmapDescriptor)
mMarker?.remove() mMarker?.remove()
mGoogleMap?.apply { mGoogleMap?.apply {
@@ -805,7 +818,7 @@ abstract class BaseGoogleMapFragment :
*/ */
fun getPolylineOptions(widthDp: Float, lineColorRes: Int): PolylineOptions { fun getPolylineOptions(widthDp: Float, lineColorRes: Int): PolylineOptions {
return PolylineOptions().clickable(false).width(AppUtils.dpToPx(widthDp)) return PolylineOptions().clickable(false).width(AppUtils.dpToPx(widthDp))
.color(ContextCompat.getColor(mContext!!, lineColorRes)).geodesic(false) .color(ContextCompat.getColor(mContext!!, lineColorRes))
} }
/** /**
@@ -824,8 +837,21 @@ abstract class BaseGoogleMapFragment :
} else { } else {
latLngList latLngList
} }
/*慢速步行epsilon=0.5,插值 2 个
日常骑行epsilon=1.0,插值 2 个
机动车行驶epsilon=1.5~2.0,插值 3 个
**/
// 1. 降噪
val filterPoints = filterNoisePoint(newLatLngList)
// 2. RDP平滑抽稀
val smoothPoints = rdpSmooth(filterPoints, epsilon = 1.0)
// 3. 插值加密线条
val finalPoints = latLngInterpolate(smoothPoints, step = 2)
//先黄黑线 //先黄黑线
getPolylineOptions(8f, R.color.line_stroke_color).let { getPolylineOptions(8f, R.color.line_stroke_color).let {
it.geodesic(true)
it.addAll(newLatLngList) it.addAll(newLatLngList)
googleMap.addPolyline(it).apply { googleMap.addPolyline(it).apply {
startCap = RoundCap() startCap = RoundCap()
@@ -841,7 +867,8 @@ abstract class BaseGoogleMapFragment :
//再画黄线 //再画黄线
getPolylineOptions(5f, R.color.rote_line_color).let { getPolylineOptions(5f, R.color.rote_line_color).let {
it.addAll(newLatLngList) it.geodesic(true)
it.addAll(finalPoints)
googleMap.addPolyline(it).apply { googleMap.addPolyline(it).apply {
startCap = RoundCap() startCap = RoundCap()
endCap = RoundCap() endCap = RoundCap()
@@ -1267,8 +1294,118 @@ abstract class BaseGoogleMapFragment :
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
mPetHeadIconBitmap?.recycle() mPetHeadIconBitmap.safeRecycle()
mPetHeadIconBitmap = null
mGoogleMapView?.onDestroy() mGoogleMapView?.onDestroy()
} }
/**
* 两点间距离 单位米
*/
private fun distanceBetween(p1: LatLng, p2: LatLng): Double {
val r = 6371000.0
val lat1 = p1.latitude * PI / 180
val lng1 = p1.longitude * PI / 180
val lat2 = p2.latitude * PI / 180
val lng2 = p2.longitude * PI / 180
val dLat = lat2 - lat1
val dLng = lng2 - lng1
val a = sin(dLat / 2).pow(2) + cos(lat1) * cos(lat2) * sin(dLng / 2).pow(2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return r * c
}
/**
* Ramer-Douglas-Peucker 轨迹抽稀平滑
* @param epsilon 阈值 建议0.5~2.0
*/
private fun rdpSmooth(points: List<LatLng>, epsilon: Double = 1.0): List<LatLng> {
if (points.size < 3) return points
var maxDist = 0.0
var index = 0
val start = points.first()
val end = points.last()
for (i in 1 until points.lastIndex) {
val dist = pointToLineDist(points[i], start, end)
if (dist > maxDist) {
maxDist = dist
index = i
}
}
return if (maxDist > epsilon) {
val left = rdpSmooth(points.subList(0, index + 1), epsilon)
val right = rdpSmooth(points.subList(index, points.size), epsilon)
left.dropLast(1) + right
} else {
listOf(start, end)
}
}
/**
* 点到线段距离
*/
private fun pointToLineDist(p: LatLng, a: LatLng, b: LatLng): Double {
val ax = a.longitude
val ay = a.latitude
val bx = b.longitude
val by = b.latitude
val px = p.longitude
val py = p.latitude
val cross = (px - ax) * (bx - ax) + (py - ay) * (by - ay)
if (cross <= 0.0) return distanceBetween(p, a)
val len2 = (bx - ax).pow(2) + (by - ay).pow(2)
if (cross >= len2) return distanceBetween(p, b)
val t = cross / len2
val nearX = ax + t * (bx - ax)
val nearY = ay + t * (by - ay)
return distanceBetween(p, LatLng(nearY, nearX))
}
/**
* 相邻两点插值补点,让线条更顺滑
* @param step 插值个数 推荐2~3
*/
private fun latLngInterpolate(points: List<LatLng>, step: Int = 2): List<LatLng> {
if (points.size < 2) return points
val result = mutableListOf<LatLng>()
for (i in 0 until points.size - 1) {
val p0 = points[i]
val p1 = points[i + 1]
result.add(p0)
for (s in 1..step) {
val ratio = s.toDouble() / (step + 1)
val lat = p0.latitude + (p1.latitude - p0.latitude) * ratio
val lng = p0.longitude + (p1.longitude - p0.longitude) * ratio
result.add(LatLng(lat, lng))
}
}
result.add(points.last())
return result
}
/**
* GPS降噪过滤跳点漂移
*/
private fun filterNoisePoint(
points: List<LatLng>, minDis: Double = 5.0, maxDis: Double = 60.0
): List<LatLng> {
if (points.isEmpty()) return emptyList()
val res = mutableListOf<LatLng>().apply { add(points.first()) }
for (i in 1 until points.size) {
val last = res.last()
val dis = distanceBetween(last, points[i])
if (dis in minDis..maxDis) {
res.add(points[i])
}
}
return res
}
} }

View File

@@ -9,9 +9,12 @@ import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.tracker.MyApplication
import com.abbidot.tracker.R import com.abbidot.tracker.R
import com.abbidot.tracker.constant.ConstantInt import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.util.ImageUtil import com.abbidot.tracker.util.ImageUtil
import com.abbidot.tracker.util.Util.Companion.safeRecycle
import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.qmuiteam.qmui.util.QMUIDisplayHelper import com.qmuiteam.qmui.util.QMUIDisplayHelper
@@ -166,4 +169,40 @@ object GoogleBitmapHelper {
vectorDrawable.draw(canvas) vectorDrawable.draw(canvas)
return BitmapDescriptorFactory.fromBitmap(bitmap) return BitmapDescriptorFactory.fromBitmap(bitmap)
} }
suspend fun getMarkerBitmapDescriptor(
context: Context,
headUrl: String,
petType: Int,
headBgResId: Int = R.drawable.pic_map_gps_avatar
): BitmapDescriptor? {
var bitmap: Bitmap? = null
var bitmapDescriptor: Any?
try {
val mapKey = if (TextUtils.isEmpty(headUrl)) {
"pet_default_head"
} else {
val idx = headUrl.lastIndexOf("/")
if (idx != -1) {
headUrl.substring(idx + 1)
} else {
headUrl
}
}
LogUtil.e("mapKey=$mapKey")
bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
bitmap = headToBitmap(context, headBgResId, headUrl, petType)
bitmap?.let {
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(it)
//保存在内存中,提高显示速度
MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor as BitmapDescriptor
}
}
} finally {
bitmap.safeRecycle()
}
// 把 return 写在 finally 外面
return bitmapDescriptor as? BitmapDescriptor
}
} }

View File

@@ -302,11 +302,10 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
if (null != mPetIconDescriptor) { if (null != mPetIconDescriptor) {
mGoogleMap?.apply { mGoogleMap?.apply {
val newLatLng = toGCJ02LatLon(latLng) val newLatLng = toGCJ02LatLon(latLng)
mMarkerOptions.position(newLatLng).icon(mPetIconDescriptor) //显示最上面,层级最高,默认值 0f
mMarkerOptions.position(newLatLng).icon(mPetIconDescriptor).zIndex(99f)
mMarker?.remove() mMarker?.remove()
mMarker = addMarker(mMarkerOptions) mMarker = addMarker(mMarkerOptions)
//显示最上面,层级最高
mMarker?.zIndex = 1f
setUserMarker() setUserMarker()
} }
} }

View File

@@ -9,11 +9,14 @@ import android.view.animation.LinearInterpolator
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.AppUtils import com.abbidot.baselibrary.util.AppUtils
import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.baselibrary.util.Utils import com.abbidot.baselibrary.util.Utils
import com.abbidot.tracker.R import com.abbidot.tracker.R
import com.abbidot.tracker.bean.HistoryDataBean import com.abbidot.tracker.bean.HistoryDataBean
import com.abbidot.tracker.bean.MapDeviceBean import com.abbidot.tracker.bean.MapDeviceBean
import com.abbidot.tracker.bean.PetBean
import com.abbidot.tracker.constant.ConstantInt import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.databinding.LayoutPetLocationInfoBinding import com.abbidot.tracker.databinding.LayoutPetLocationInfoBinding
import com.abbidot.tracker.util.Util import com.abbidot.tracker.util.Util
@@ -24,6 +27,7 @@ import com.google.android.gms.maps.model.Circle
import com.google.android.gms.maps.model.CircleOptions import com.google.android.gms.maps.model.CircleOptions
import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.gms.maps.model.Polyline import com.google.android.gms.maps.model.Polyline
import kotlin.math.pow import kotlin.math.pow
@@ -59,6 +63,8 @@ class HomeMapGoogleMapFragmentV3 : BaseGoogleMapFragment() {
//是否需要反编译地理位置 //是否需要反编译地理位置
private var isReverseGeocode = false private var isReverseGeocode = false
//其他没有选择的宠物
private val mOtherPetMarkerList = mutableListOf<Marker?>()
companion object { companion object {
@JvmStatic @JvmStatic
@@ -358,6 +364,39 @@ class HomeMapGoogleMapFragmentV3 : BaseGoogleMapFragment() {
mRippleCircle = null mRippleCircle = null
} }
/**
* 显示其他宠物的头像Marker
*/
suspend fun showOtherPetHeadMarker(
petList: MutableList<PetBean>, headBgResId: Int = R.drawable.pic_map_gps_avatar
) {
clearOtherPetHeadMarker()
if (MMKVUtil.getBoolean(MMKVKey.ShowAllPet)) {
for (pet in petList) {
val bitmapDescriptor = GoogleBitmapHelper.getMarkerBitmapDescriptor(
mContext!!, pet.imgurl, pet.petType, headBgResId
)
val newLatLng = toGCJ02LatLon(LatLng(pet.latitude, pet.longitude))
val markerOptions = MarkerOptions().position(newLatLng)
markerOptions.icon(bitmapDescriptor)
mGoogleMap?.apply {
mOtherPetMarkerList.add(addMarker(markerOptions)?.apply {
})
}
}
}
}
/**
* 清空其他宠物的头像Marker
*/
fun clearOtherPetHeadMarker() {
for (m in mOtherPetMarkerList) {
m?.remove()
}
mOtherPetMarkerList.clear()
}
override fun onDetach() { override fun onDetach() {
super.onDetach() super.onDetach()
clearRippleCircleAnim() clearRippleCircleAnim()

View File

@@ -5,6 +5,7 @@ import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.location.LocationManager import android.location.LocationManager
import android.os.Build import android.os.Build
import android.os.Parcelable import android.os.Parcelable
@@ -900,6 +901,12 @@ class Util {
return this.code in 0x4E00..0x9FA5 return this.code in 0x4E00..0x9FA5
} }
/**
* 扩展函数Bitmap单独安全回收
*/
fun Bitmap?.safeRecycle() {
this?.takeIf { !it.isRecycled }?.recycle()
}
} }

View File

@@ -921,8 +921,9 @@ class ViewUtil private constructor() {
fun getMapTypeDialog( fun getMapTypeDialog(
context: Context, context: Context,
onItemClickListener: BaseRecyclerAdapter.OnItemClickListener? = null, onItemClickListener: BaseRecyclerAdapter.OnItemClickListener? = null,
fenceCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null, fenceCheckedListener: CompoundButton.OnCheckedChangeListener? = null,
dashedCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null dashedCheckedListener: CompoundButton.OnCheckedChangeListener? = null,
showAllPetCheckedListener: CompoundButton.OnCheckedChangeListener? = null
): SelectMapTypeDialog { ): SelectMapTypeDialog {
val mapType = Util.getMapTypeSp() val mapType = Util.getMapTypeSp()
val typeList = mutableListOf<DataBean>() val typeList = mutableListOf<DataBean>()
@@ -947,7 +948,7 @@ class ViewUtil private constructor() {
}) })
} }
return SelectMapTypeDialog( return SelectMapTypeDialog(
context, mapTypeAdapter, fenceCheckedChangeListener, dashedCheckedChangeListener context, mapTypeAdapter, fenceCheckedListener, dashedCheckedListener,showAllPetCheckedListener
) )
} }

View File

@@ -9,6 +9,7 @@ interface OnCircleViewScaleClickListener {
fun circleScaleEndClick() fun circleScaleEndClick()
fun circleStartDragClick() fun circleStartDragClick()
fun circleScalingClick() fun circleScalingClick()
fun circleMinSizeClick()
fun circleEndDragClick() fun circleEndDragClick()
} }
@@ -17,11 +18,13 @@ interface OnRectViewRotateScaleClickListener {
fun rectScaleEndClick() fun rectScaleEndClick()
fun rectScalingClick() fun rectScalingClick()
fun rectStartDragClick() fun rectStartDragClick()
fun rectMinSizeClick()
fun rectEndDragClick() fun rectEndDragClick()
} }
interface OnPolygonViewPointPullClickListener { interface OnPolygonViewPointPullClickListener {
fun polygonPointPullClick() fun polygonPointPullClick()
fun polygonStartDragClick() fun polygonStartDragClick()
fun polygonMinSizeClick()
fun polygonEndDragClick() fun polygonEndDragClick()
} }

View File

@@ -380,6 +380,7 @@ class FencesCircleView : View {
if (mLimitRadius == 0f && circleRadius < mLocationBitmap!!.height / 2f) { if (mLimitRadius == 0f && circleRadius < mLocationBitmap!!.height / 2f) {
return true return true
} else if (circleRadius < mLimitRadius) { } else if (circleRadius < mLimitRadius) {
mScaleClickListener?.circleMinSizeClick()
return true return true
} }
mMaxCircleRadius = circleRadius mMaxCircleRadius = circleRadius

View File

@@ -339,6 +339,7 @@ class FencesPolygonView : View {
} }
val tempRect = getTempRect(mTouchPointTag, mTempPoint) val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) { if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false return false
} }
mOutHideRectWidth = tempRect.width() mOutHideRectWidth = tempRect.width()
@@ -401,6 +402,7 @@ class FencesPolygonView : View {
} }
val tempRect = getTempRect(mTouchPointTag, mTempPoint) val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) { if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false return false
} }
mOutHideRectWidth = tempRect.width() mOutHideRectWidth = tempRect.width()
@@ -462,6 +464,7 @@ class FencesPolygonView : View {
} }
val tempRect = getTempRect(mTouchPointTag, mTempPoint) val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) { if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false return false
} }
mOutHideRectWidth = tempRect.width() mOutHideRectWidth = tempRect.width()
@@ -523,6 +526,7 @@ class FencesPolygonView : View {
} }
val tempRect = getTempRect(mTouchPointTag, mTempPoint) val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) { if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false return false
} }
mOutHideRectWidth = tempRect.width() mOutHideRectWidth = tempRect.width()
@@ -584,6 +588,7 @@ class FencesPolygonView : View {
} }
val tempRect = getTempRect(mTouchPointTag, mTempPoint) val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) { if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false return false
} }
mOutHideRectWidth = tempRect.width() mOutHideRectWidth = tempRect.width()
@@ -645,6 +650,7 @@ class FencesPolygonView : View {
} }
val tempRect = getTempRect(mTouchPointTag, mTempPoint) val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) { if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false return false
} }
mOutHideRectWidth = tempRect.width() mOutHideRectWidth = tempRect.width()

View File

@@ -500,6 +500,7 @@ class FencesRectView : View {
return true return true
} }
} else if (newRectWidth < mLimitSide || newRectHeight < mLimitSide) { } else if (newRectWidth < mLimitSide || newRectHeight < mLimitSide) {
mRotateScaleClickListener?.rectMinSizeClick()
return true return true
} }
mRotateScaleClickListener?.rectScalingClick() mRotateScaleClickListener?.rectScalingClick()

View File

@@ -0,0 +1,22 @@
package com.abbidot.tracker.widget
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.View
/**
*Created by .yzq on 2026/5/25/周一.
* @link
* @description:
*/
class TrackLineView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
}
}

View File

@@ -66,6 +66,33 @@
android:id="@+id/cb_dialog_map_dashed_line_switch" android:id="@+id/cb_dialog_map_dashed_line_switch"
style="@style/my_checkbox_switch_style" style="@style/my_checkbox_switch_style"
android:layout_width="@dimen/dp_47" android:layout_width="@dimen/dp_47"
android:checked="true" /> android:checked="false" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_map_type_show_all_pet_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ll_map_type_show_dashed_line_switch"
android:layout_marginTop="@dimen/dp_8"
android:background="@drawable/shape8_gray_border_transparent_bg"
android:gravity="center_vertical"
android:minHeight="@dimen/dp_47"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/dp_16">
<com.abbidot.tracker.widget.TypefaceTextView
style="@style/my_TextView_style_v2"
android:layout_weight="1"
android:gravity="start"
android:text="@string/txt_show_all_pet"
android:textSize="@dimen/textSize14"
app:typeface="@string/roboto_bold_font" />
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/cb_dialog_map_all_pet_switch"
style="@style/my_checkbox_switch_style"
android:layout_width="@dimen/dp_47"
android:checked="false" />
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
</RelativeLayout> </RelativeLayout>

View File

@@ -1012,5 +1012,7 @@
<string name="txt_package_permission3">1. VIP-Prioritätssupport\n2. Schneller Austauschservice (1x inbegriffen)\n3. Erweiterter Schutz\n4. Sorgenfreier Haustierschutz</string> <string name="txt_package_permission3">1. VIP-Prioritätssupport\n2. Schneller Austauschservice (1x inbegriffen)\n3. Erweiterter Schutz\n4. Sorgenfreier Haustierschutz</string>
<string name="txt_add_wifi_tips">Fügen Sie eine Wi-Fi-Zone hinzu, um die Betriebszeit zu verlängern.</string> <string name="txt_add_wifi_tips">Fügen Sie eine Wi-Fi-Zone hinzu, um die Betriebszeit zu verlängern.</string>
<string name="txt_add">Hinzufügen</string> <string name="txt_add">Hinzufügen</string>
<string name="txt_show_all_pet">Alle Haustiere anzeigen</string>
<string name="txt_already_min_size">Bereits auf Minimalgröße</string>
</resources> </resources>

View File

@@ -1056,5 +1056,7 @@
<string name="txt_package_permission3">1. VIP 优先支持\n2. 快速更换服务含1次\n3. 增强型保障\n4. 无忧宠物保护</string> <string name="txt_package_permission3">1. VIP 优先支持\n2. 快速更换服务含1次\n3. 增强型保障\n4. 无忧宠物保护</string>
<string name="txt_add_wifi_tips">添加wifi区域可延长使用时长</string> <string name="txt_add_wifi_tips">添加wifi区域可延长使用时长</string>
<string name="txt_add">去添加</string> <string name="txt_add">去添加</string>
<string name="txt_show_all_pet">显示所有宠物</string>
<string name="txt_already_min_size">已是最小尺寸</string>
</resources> </resources>

View File

@@ -1114,5 +1114,7 @@
<string name="txt_package_permission3">1.VIP priority support\n2.Fast replacement service (1 included)\n3.Enhanced protection\n4.Hassle-free pet protection</string> <string name="txt_package_permission3">1.VIP priority support\n2.Fast replacement service (1 included)\n3.Enhanced protection\n4.Hassle-free pet protection</string>
<string name="txt_add_wifi_tips">Add Wi-Fi zone to prolong care time</string> <string name="txt_add_wifi_tips">Add Wi-Fi zone to prolong care time</string>
<string name="txt_add">Add</string> <string name="txt_add">Add</string>
<string name="txt_show_all_pet">Display All Pets</string>
<string name="txt_already_min_size">Already at minimum size</string>
</resources> </resources>

View File

@@ -38,6 +38,7 @@ import androidx.annotation.StringDef
MMKVKey.MapType, MMKVKey.MapType,
MMKVKey.ShowFence, MMKVKey.ShowFence,
MMKVKey.ShowDashedLine, MMKVKey.ShowDashedLine,
MMKVKey.ShowAllPet,
MMKVKey.isCrash, MMKVKey.isCrash,
MMKVKey.AvailableOrder, MMKVKey.AvailableOrder,
MMKVKey.isFirstCheckBleOpen, MMKVKey.isFirstCheckBleOpen,
@@ -94,6 +95,7 @@ annotation class MMKVKey {
//map页是否显示围栏 //map页是否显示围栏
const val ShowFence = "isShowFence" const val ShowFence = "isShowFence"
const val ShowDashedLine = "isShowDashedLine" const val ShowDashedLine = "isShowDashedLine"
const val ShowAllPet = "isShowAllPet"
//是首次打开APP //是首次打开APP
const val FirstOpen = "firstOpen" const val FirstOpen = "firstOpen"