1.优化订阅列表显示(Renew at $29.97 per month thereafter)
2.历史轨迹地图增加罗盘和当前手机位置显示 3.历史轨迹点模式增加点击可显示宠物信息 4.修复雷达停止退出还会有声音的bug
This commit is contained in:
@@ -28,9 +28,9 @@ android {
|
|||||||
applicationId "com.abbidot.tracker"
|
applicationId "com.abbidot.tracker"
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 35
|
targetSdkVersion 35
|
||||||
versionCode 2109
|
versionCode 2110
|
||||||
// versionName "2.1.9"
|
// versionName "2.1.10"
|
||||||
versionName "2.1.9-Beta1"
|
versionName "2.1.10-Beta1"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
|||||||
@@ -47,19 +47,34 @@ class MySubscriptionAdapter(
|
|||||||
holder.setText(R.id.tv_my_subscription_plan_name, item.mealName)
|
holder.setText(R.id.tv_my_subscription_plan_name, item.mealName)
|
||||||
holder.getTextView(R.id.tv_my_subscription_auto_subscription_tips).apply {
|
holder.getTextView(R.id.tv_my_subscription_auto_subscription_tips).apply {
|
||||||
visibility = if (item.subscriptionStatus == ConstantInt.Open) {
|
visibility = if (item.subscriptionStatus == ConstantInt.Open) {
|
||||||
text = if (item.mealUnit == ConstantString.PackageUnitYear) String.format(
|
text = when (item.mealUnit) {
|
||||||
mContext.getString(R.string.txt_auto_subscription_year),
|
ConstantString.PackageUnitYear -> if (item.mealPeriod > 1) String.format(
|
||||||
item.autoRenewPrice.toString()
|
mContext.getString(R.string.txt_auto_subscription_years),
|
||||||
)
|
|
||||||
else if (item.mealUnit == ConstantString.PackageUnitDay) String.format(
|
|
||||||
mContext.getString(R.string.txt_auto_subscription_day),
|
|
||||||
"${item.autoRenewPrice}",
|
"${item.autoRenewPrice}",
|
||||||
"${item.mealPeriod}"
|
"${item.mealPeriod}"
|
||||||
|
) else String.format(
|
||||||
|
mContext.getString(R.string.txt_auto_subscription_year),
|
||||||
|
"${item.autoRenewPrice}"
|
||||||
)
|
)
|
||||||
else String.format(
|
|
||||||
|
ConstantString.PackageUnitDay -> if (item.mealPeriod > 1) String.format(
|
||||||
|
mContext.getString(R.string.txt_auto_subscription_days),
|
||||||
|
"${item.autoRenewPrice}",
|
||||||
|
"${item.mealPeriod}"
|
||||||
|
) else String.format(
|
||||||
|
mContext.getString(R.string.txt_auto_subscription_day),
|
||||||
|
"${item.autoRenewPrice}"
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> if (item.mealPeriod > 1) String.format(
|
||||||
|
mContext.getString(R.string.txt_auto_subscription_months),
|
||||||
|
"${item.autoRenewPrice}",
|
||||||
|
"${item.mealPeriod}"
|
||||||
|
) else String.format(
|
||||||
mContext.getString(R.string.txt_auto_subscription_month),
|
mContext.getString(R.string.txt_auto_subscription_month),
|
||||||
item.autoRenewPrice.toString()
|
"${item.autoRenewPrice}"
|
||||||
)
|
)
|
||||||
|
}
|
||||||
View.VISIBLE
|
View.VISIBLE
|
||||||
} else View.GONE
|
} else View.GONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class HistoryDataActivity :
|
|||||||
|
|
||||||
mHistoryDataMapCommon = HistoryDataMapCommon()
|
mHistoryDataMapCommon = HistoryDataMapCommon()
|
||||||
val fragment = mHistoryDataMapCommon.getMapFragment(
|
val fragment = mHistoryDataMapCommon.getMapFragment(
|
||||||
this, MapMarkerInfoView(mContext), mGeoCoderViewModel
|
this, MapMarkerInfoView(mContext), mGeoCoderViewModel,VerticalTopToBottomSeekBar(mContext)
|
||||||
) {}
|
) {}
|
||||||
supportFragmentManager.commit {
|
supportFragmentManager.commit {
|
||||||
add(R.id.history_data_map_view, fragment)
|
add(R.id.history_data_map_view, fragment)
|
||||||
|
|||||||
@@ -1088,6 +1088,7 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
|
|||||||
mFindBleDeviceViewModel.stopPlay()
|
mFindBleDeviceViewModel.stopPlay()
|
||||||
showAndHideFindLayout()
|
showAndHideFindLayout()
|
||||||
} else {
|
} else {
|
||||||
|
mFindBleDeviceViewModel.stopPlay()
|
||||||
mFindBleDeviceViewModel.stopFindDevice()
|
mFindBleDeviceViewModel.stopFindDevice()
|
||||||
finishActivity()
|
finishActivity()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.abbidot.tracker.ui.fragment.map.baidumap.HistoryDataBaiduMapFragment
|
|||||||
import com.abbidot.tracker.ui.fragment.map.googlemap.HistoryDataGoogleMapFragment
|
import com.abbidot.tracker.ui.fragment.map.googlemap.HistoryDataGoogleMapFragment
|
||||||
import com.abbidot.tracker.vm.GeoCoderViewModel
|
import com.abbidot.tracker.vm.GeoCoderViewModel
|
||||||
import com.abbidot.tracker.widget.MapMarkerInfoView
|
import com.abbidot.tracker.widget.MapMarkerInfoView
|
||||||
|
import com.abbidot.tracker.widget.VerticalTopToBottomSeekBar
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*Created by .yzq on 2022/7/5/005.
|
*Created by .yzq on 2022/7/5/005.
|
||||||
@@ -25,6 +26,7 @@ class HistoryDataMapCommon : BaseMapCommon() {
|
|||||||
context: Context,
|
context: Context,
|
||||||
markerInfoView: MapMarkerInfoView,
|
markerInfoView: MapMarkerInfoView,
|
||||||
geoCoderViewModel: GeoCoderViewModel,
|
geoCoderViewModel: GeoCoderViewModel,
|
||||||
|
verticalTopToBottomSeekBar: VerticalTopToBottomSeekBar,
|
||||||
mapLoadOk: () -> Unit
|
mapLoadOk: () -> Unit
|
||||||
): Fragment {
|
): Fragment {
|
||||||
return if (AppUtils.isChina(AppUtils.SWITCH_MAP_TYPE)) {
|
return if (AppUtils.isChina(AppUtils.SWITCH_MAP_TYPE)) {
|
||||||
@@ -33,7 +35,7 @@ class HistoryDataMapCommon : BaseMapCommon() {
|
|||||||
mHistoryDataBaiduMapFragment!!
|
mHistoryDataBaiduMapFragment!!
|
||||||
} else {
|
} else {
|
||||||
mHistoryDataGoogleMapFragment = HistoryDataGoogleMapFragment.newInstance(
|
mHistoryDataGoogleMapFragment = HistoryDataGoogleMapFragment.newInstance(
|
||||||
context, markerInfoView, geoCoderViewModel, mapLoadOk
|
context, markerInfoView, geoCoderViewModel, verticalTopToBottomSeekBar, mapLoadOk
|
||||||
)
|
)
|
||||||
mHistoryDataGoogleMapFragment!!
|
mHistoryDataGoogleMapFragment!!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class RouteV2Fragment : BaseFragment<FragmentRouteV2Binding>(FragmentRouteV2Bind
|
|||||||
root, WindowInsetsCompat.Type.statusBars()
|
root, WindowInsetsCompat.Type.statusBars()
|
||||||
)
|
)
|
||||||
mFragment = mHistoryDataMapCommon.getMapFragment(
|
mFragment = mHistoryDataMapCommon.getMapFragment(
|
||||||
mContext!!, miHomeRouteAddressView, mGeoCoderViewModel
|
mContext!!, miHomeRouteAddressView, mGeoCoderViewModel, vsbMapRouteLine
|
||||||
) {
|
) {
|
||||||
mapLoadOk()
|
mapLoadOk()
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ class RouteV2Fragment : BaseFragment<FragmentRouteV2Binding>(FragmentRouteV2Bind
|
|||||||
when (checkedId) {
|
when (checkedId) {
|
||||||
mViewBinding.rbHistoryRouteMapLine.id -> {
|
mViewBinding.rbHistoryRouteMapLine.id -> {
|
||||||
MMKVUtil.putInt(MMKVKey.LineSelectPosition, ConstantInt.Line)
|
MMKVUtil.putInt(MMKVKey.LineSelectPosition, ConstantInt.Line)
|
||||||
mHistoryDataMapCommon.addSetLine(false)
|
mHistoryDataMapCommon.addSetLine()
|
||||||
if (mHistoryDataList.size > 0) {
|
if (mHistoryDataList.size > 0) {
|
||||||
// mHistoryDataMapCommon.slideStopChanged(mHistoryDataList.size - 1)
|
// mHistoryDataMapCommon.slideStopChanged(mHistoryDataList.size - 1)
|
||||||
} else {
|
} else {
|
||||||
@@ -198,7 +198,7 @@ class RouteV2Fragment : BaseFragment<FragmentRouteV2Binding>(FragmentRouteV2Bind
|
|||||||
|
|
||||||
mViewBinding.rbHistoryRouteMapNumber.id -> {
|
mViewBinding.rbHistoryRouteMapNumber.id -> {
|
||||||
MMKVUtil.putInt(MMKVKey.LineSelectPosition, ConstantInt.Point)
|
MMKVUtil.putInt(MMKVKey.LineSelectPosition, ConstantInt.Point)
|
||||||
mHistoryDataMapCommon.addSetNumber(false)
|
mHistoryDataMapCommon.addSetNumber()
|
||||||
if (mHistoryDataList.size > 0) {
|
if (mHistoryDataList.size > 0) {
|
||||||
// mHistoryDataMapCommon.slideStopChanged(mHistoryDataList.size - 1)
|
// mHistoryDataMapCommon.slideStopChanged(mHistoryDataList.size - 1)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -301,9 +301,11 @@ class HomeTrackFragment :
|
|||||||
// mTrackerDFUStateDialog?.addData(this)
|
// mTrackerDFUStateDialog?.addData(this)
|
||||||
if (progress == 100) {
|
if (progress == 100) {
|
||||||
LogUtil.e("固件下载完成")
|
LogUtil.e("固件下载完成")
|
||||||
mDeviceDFUViewModel.startDFU(
|
mBleTrackDeviceBean?.let { b ->
|
||||||
mContext!!, mBleTrackDeviceBean!!.bleDevice!!, filePath
|
b.bleDevice?.let { ble ->
|
||||||
)
|
mDeviceDFUViewModel.startDFU(mContext!!, ble, filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -830,7 +832,7 @@ class HomeTrackFragment :
|
|||||||
mBleTrackDeviceBean?.apply {
|
mBleTrackDeviceBean?.apply {
|
||||||
SRBleUtil.instance.disconnectToMac(mac)
|
SRBleUtil.instance.disconnectToMac(mac)
|
||||||
}
|
}
|
||||||
mMapDeviceBean?.let {ble->
|
mMapDeviceBean?.let { ble ->
|
||||||
mTrackerSetViewModel.turnOff(ble.deviceServerId)
|
mTrackerSetViewModel.turnOff(ble.deviceServerId)
|
||||||
}
|
}
|
||||||
mTrackMenuList[1].menuValue = ""
|
mTrackMenuList[1].menuValue = ""
|
||||||
|
|||||||
@@ -212,7 +212,6 @@ class FencesAddEditBaiduMapFragment : BaseBaiduMapFragment() {
|
|||||||
*/
|
*/
|
||||||
fun resetFencesViewCentre(centreLatLng: LatLng) {
|
fun resetFencesViewCentre(centreLatLng: LatLng) {
|
||||||
mBaiduMap?.projection?.toScreenLocation(centreLatLng)?.apply {
|
mBaiduMap?.projection?.toScreenLocation(centreLatLng)?.apply {
|
||||||
LogUtil.e("ddddssddfsdf$this")
|
|
||||||
if (mFencesCircleView.isVisible) {
|
if (mFencesCircleView.isVisible) {
|
||||||
mFencesCircleView.setCentreXY(this)
|
mFencesCircleView.setCentreXY(this)
|
||||||
} else if (mFencesRectView.isVisible) {
|
} else if (mFencesRectView.isVisible) {
|
||||||
|
|||||||
@@ -868,6 +868,7 @@ abstract class BaseGoogleMapFragment :
|
|||||||
) {
|
) {
|
||||||
//启用“我的位置”图层。
|
//启用“我的位置”图层。
|
||||||
isMyLocationEnabled = true
|
isMyLocationEnabled = true
|
||||||
|
}
|
||||||
uiSettings.let {
|
uiSettings.let {
|
||||||
//禁止显示“我的位置”按钮。
|
//禁止显示“我的位置”按钮。
|
||||||
it.isMyLocationButtonEnabled = false
|
it.isMyLocationButtonEnabled = false
|
||||||
@@ -878,7 +879,6 @@ abstract class BaseGoogleMapFragment :
|
|||||||
it.isTiltGesturesEnabled = false
|
it.isTiltGesturesEnabled = false
|
||||||
it.isCompassEnabled = false
|
it.isCompassEnabled = false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
onMapLoadOk(googleMap)
|
onMapLoadOk(googleMap)
|
||||||
}
|
}
|
||||||
@@ -961,9 +961,9 @@ abstract class BaseGoogleMapFragment :
|
|||||||
* 由 points 组成的区域 距离手机屏幕距离screenPaddingPx 像素单位
|
* 由 points 组成的区域 距离手机屏幕距离screenPaddingPx 像素单位
|
||||||
*/
|
*/
|
||||||
fun setLatLngZoom(context: Context, screenPaddingPx: Int, vararg points: LatLng) {
|
fun setLatLngZoom(context: Context, screenPaddingPx: Int, vararg points: LatLng) {
|
||||||
val width = context.resources.displayMetrics.widthPixels
|
// val width = context.resources.displayMetrics.widthPixels
|
||||||
val height = context.resources.displayMetrics.heightPixels
|
// val height = context.resources.displayMetrics.heightPixels
|
||||||
val padding = width / 8
|
// val padding = width / 8
|
||||||
val builder = LatLngBounds.Builder()
|
val builder = LatLngBounds.Builder()
|
||||||
for (item in points) {
|
for (item in points) {
|
||||||
builder.include(item)
|
builder.include(item)
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package com.abbidot.tracker.ui.fragment.map.googlemap
|
package com.abbidot.tracker.ui.fragment.map.googlemap
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.abbidot.baselibrary.constant.MMKVKey
|
import com.abbidot.baselibrary.constant.MMKVKey
|
||||||
import com.abbidot.baselibrary.util.AppUtils
|
import com.abbidot.baselibrary.util.AppUtils
|
||||||
@@ -18,6 +15,7 @@ import com.abbidot.tracker.constant.ConstantInt
|
|||||||
import com.abbidot.tracker.util.ViewUtil
|
import com.abbidot.tracker.util.ViewUtil
|
||||||
import com.abbidot.tracker.vm.GeoCoderViewModel
|
import com.abbidot.tracker.vm.GeoCoderViewModel
|
||||||
import com.abbidot.tracker.widget.MapMarkerInfoView
|
import com.abbidot.tracker.widget.MapMarkerInfoView
|
||||||
|
import com.abbidot.tracker.widget.VerticalTopToBottomSeekBar
|
||||||
import com.google.android.gms.maps.GoogleMap
|
import com.google.android.gms.maps.GoogleMap
|
||||||
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
|
||||||
@@ -34,6 +32,7 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
|
|||||||
// private val mScreenshotsViewModel: ScreenshotsViewModel by viewModels()
|
// private val mScreenshotsViewModel: ScreenshotsViewModel by viewModels()
|
||||||
private lateinit var mGeoCoderViewModel: GeoCoderViewModel
|
private lateinit var mGeoCoderViewModel: GeoCoderViewModel
|
||||||
private lateinit var mMarkerInfoView: MapMarkerInfoView
|
private lateinit var mMarkerInfoView: MapMarkerInfoView
|
||||||
|
private lateinit var mVerticalTopToBottomSeekBar: VerticalTopToBottomSeekBar
|
||||||
|
|
||||||
private val mMarkerOptions = MarkerOptions()
|
private val mMarkerOptions = MarkerOptions()
|
||||||
private lateinit var mPetIconDescriptor: BitmapDescriptor
|
private lateinit var mPetIconDescriptor: BitmapDescriptor
|
||||||
@@ -56,10 +55,12 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
|
|||||||
context: Context,
|
context: Context,
|
||||||
markerInfoView: MapMarkerInfoView,
|
markerInfoView: MapMarkerInfoView,
|
||||||
geoCoderViewModel: GeoCoderViewModel,
|
geoCoderViewModel: GeoCoderViewModel,
|
||||||
|
verticalTopToBottomSeekBar: VerticalTopToBottomSeekBar,
|
||||||
mapLoadOk: () -> Unit
|
mapLoadOk: () -> Unit
|
||||||
) = HistoryDataGoogleMapFragment().apply {
|
) = HistoryDataGoogleMapFragment().apply {
|
||||||
mContext = context
|
mContext = context
|
||||||
mGeoCoderViewModel = geoCoderViewModel
|
mGeoCoderViewModel = geoCoderViewModel
|
||||||
|
mVerticalTopToBottomSeekBar = verticalTopToBottomSeekBar
|
||||||
mMarkerInfoView = markerInfoView
|
mMarkerInfoView = markerInfoView
|
||||||
mMapLoadOk = mapLoadOk
|
mMapLoadOk = mapLoadOk
|
||||||
}
|
}
|
||||||
@@ -100,17 +101,22 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
|
|||||||
if (mMarkerInfoView.isVisible) setMarkerInfoViewOffset(mMarkerInfoView, it)
|
if (mMarkerInfoView.isVisible) setMarkerInfoViewOffset(mMarkerInfoView, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setOnMarkerClickListener { m ->
|
||||||
//检测权限,开启用户的当前位置https://developers.google.cn/maps/documentation/android-sdk/location?hl=zh-cn
|
//点击数字大头针回到该位置
|
||||||
if (ActivityCompat.checkSelfPermission(
|
if (m != mMarker) {
|
||||||
mContext!!, Manifest.permission.ACCESS_FINE_LOCATION
|
val index = m.tag
|
||||||
) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
|
if (index is Int) {
|
||||||
mContext!!, Manifest.permission.ACCESS_COARSE_LOCATION
|
mVerticalTopToBottomSeekBar.progress = index
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
mMarker?.let {
|
||||||
) {
|
it.position = m.position
|
||||||
//启用“我的位置”图层。
|
|
||||||
isMyLocationEnabled = false
|
|
||||||
}
|
}
|
||||||
|
slideStopChanged(index)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
}
|
||||||
|
|
||||||
|
uiSettings.isCompassEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
getLastLocation()
|
getLastLocation()
|
||||||
@@ -267,7 +273,9 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
it.addMarker(numberMarker)
|
it.addMarker(numberMarker)?.apply {
|
||||||
|
tag = i
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//设置宠物头像和经纬度
|
//设置宠物头像和经纬度
|
||||||
|
|||||||
757
app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt
Normal file
757
app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt
Normal file
@@ -0,0 +1,757 @@
|
|||||||
|
package com.abbidot.tracker.widget
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.DashPathEffect
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Point
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.abbidot.baselibrary.util.LogUtil
|
||||||
|
import com.abbidot.tracker.R
|
||||||
|
import com.abbidot.tracker.util.ImageUtil
|
||||||
|
import com.abbidot.tracker.util.ViewUtil
|
||||||
|
import com.abbidot.tracker.widget.FencesPolygonView.Companion.POINT_A
|
||||||
|
import com.abbidot.tracker.widget.FencesPolygonView.Companion.POINT_B
|
||||||
|
import com.abbidot.tracker.widget.FencesPolygonView.Companion.POINT_C
|
||||||
|
import com.abbidot.tracker.widget.FencesPolygonView.Companion.POINT_D
|
||||||
|
import com.qmuiteam.qmui.util.QMUIDisplayHelper
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.atan2
|
||||||
|
import kotlin.math.cos
|
||||||
|
import kotlin.math.pow
|
||||||
|
import kotlin.math.sin
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
/**
|
||||||
|
*Created by .yzq on 2022/1/22/022.
|
||||||
|
* @link
|
||||||
|
* @description:添加围栏矩形,只针对围栏
|
||||||
|
*
|
||||||
|
* 按以下点组成的矩形
|
||||||
|
* A B
|
||||||
|
* D C
|
||||||
|
*/
|
||||||
|
class FencesRectView2 : View {
|
||||||
|
|
||||||
|
private lateinit var mContext: Context
|
||||||
|
private var mWidth = 0
|
||||||
|
private var mHeight = 0
|
||||||
|
|
||||||
|
private lateinit var mStrokePaint: Paint
|
||||||
|
private lateinit var mFillPaint: Paint
|
||||||
|
|
||||||
|
private lateinit var mRectF: RectF
|
||||||
|
|
||||||
|
//矩形的宽高
|
||||||
|
private var mRectWidth = 0f
|
||||||
|
private var mRectHeight = 0f
|
||||||
|
private var mRectLeft = 0f
|
||||||
|
private var mRectRight = 0f
|
||||||
|
private var mRectTop = 0f
|
||||||
|
private var mRectBottom = 0f
|
||||||
|
|
||||||
|
//中心坐标点
|
||||||
|
private var mCentreX = 0f
|
||||||
|
private var mCentreY = 0f
|
||||||
|
|
||||||
|
//定位图标
|
||||||
|
private var mLocationBitmap: Bitmap? = null
|
||||||
|
private var mDotBitmap: Bitmap? = null
|
||||||
|
private var mZoomBitmap: Bitmap? = null
|
||||||
|
private var mRotateBitmap: Bitmap? = null
|
||||||
|
|
||||||
|
private var mDashedColor = 0
|
||||||
|
private var mFillBgColor = 0
|
||||||
|
|
||||||
|
private var isRotate = false
|
||||||
|
private var isScale = false
|
||||||
|
private var isDrag = false
|
||||||
|
private var mOldX = 0f
|
||||||
|
private var mOldY = 0f
|
||||||
|
|
||||||
|
//是否显示距离
|
||||||
|
private var isShowDistance = true
|
||||||
|
private var mRectWidthDistanceText = ""
|
||||||
|
private var mRectHeightDistanceText = ""
|
||||||
|
private lateinit var mTextPaint: Paint
|
||||||
|
|
||||||
|
//旋转的角度(真实的角度单位)
|
||||||
|
private var mRotateAngle = 0.0
|
||||||
|
|
||||||
|
//计算画布移动后,点击旋转图片按钮一开始的坐标角度,经过平移坐标轴算出
|
||||||
|
private var mStartRotateAngle = 0.0
|
||||||
|
|
||||||
|
//是否需要测量,防止百度地图使用会执行onMeasure/地图页面上下滑动重新布局执行onMeasure
|
||||||
|
private var isNeedMeasure = true
|
||||||
|
|
||||||
|
//限制边长
|
||||||
|
var mLimitSide = 0f
|
||||||
|
|
||||||
|
private var mRotateScaleClickListener: OnRectViewRotateScaleClickListener? = null
|
||||||
|
|
||||||
|
constructor(context: Context) : super(context) {
|
||||||
|
init(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||||
|
context, attrs, defStyleAttr
|
||||||
|
) {
|
||||||
|
init(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||||
|
init(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init(context: Context) {
|
||||||
|
//禁用硬件加速,防止返回到界面多次执行onDraw
|
||||||
|
setLayerType(LAYER_TYPE_SOFTWARE, null)
|
||||||
|
mContext = context
|
||||||
|
// mRectWidth = QMUIDisplayHelper.dpToPx(100).toFloat()
|
||||||
|
// mRectHeight = QMUIDisplayHelper.dpToPx(100).toFloat()
|
||||||
|
|
||||||
|
mStrokePaint = Paint()
|
||||||
|
LogUtil.e("${javaClass.canonicalName}------------------->>>init")
|
||||||
|
mStrokePaint.style = Paint.Style.STROKE
|
||||||
|
mStrokePaint.strokeWidth = QMUIDisplayHelper.dpToPx(1).toFloat()
|
||||||
|
//虚线长度
|
||||||
|
val dashPathWidth = QMUIDisplayHelper.dpToPx(6).toFloat()
|
||||||
|
mStrokePaint.pathEffect = DashPathEffect(floatArrayOf(dashPathWidth, dashPathWidth), 0f)
|
||||||
|
mStrokePaint.color = ContextCompat.getColor(mContext, R.color.select_color)
|
||||||
|
|
||||||
|
mFillPaint = Paint()
|
||||||
|
mFillPaint.style = Paint.Style.FILL
|
||||||
|
mFillPaint.color = ContextCompat.getColor(mContext, R.color.select_color4)
|
||||||
|
|
||||||
|
mTextPaint = Paint()
|
||||||
|
val tf =
|
||||||
|
ViewUtil.instance.setTypeface(context, context.getString(R.string.number_din_cond_font))
|
||||||
|
mTextPaint.typeface = tf
|
||||||
|
mTextPaint.textSize = QMUIDisplayHelper.sp2px(context, 14).toFloat()
|
||||||
|
mTextPaint.color = ContextCompat.getColor(mContext, R.color.black)
|
||||||
|
|
||||||
|
mDotBitmap = ImageUtil.getBitmapFromDrawableAndSvg(context, R.drawable.icon_fence_dot_svg)
|
||||||
|
mLocationBitmap =
|
||||||
|
ImageUtil.getBitmapFromDrawableAndSvg(context, R.drawable.icon_fences_zone_gps_svg)
|
||||||
|
mZoomBitmap =
|
||||||
|
ImageUtil.getBitmapFromDrawableAndSvg(context, R.drawable.icon_fence_rect_zoom_svg)
|
||||||
|
mRotateBitmap =
|
||||||
|
ImageUtil.getBitmapFromDrawableAndSvg(context, R.drawable.icon_fence_rect_rotate_svg)
|
||||||
|
|
||||||
|
mRectF = RectF()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||||
|
mWidth = measuredWidth
|
||||||
|
mHeight = measuredHeight
|
||||||
|
LogUtil.e("${javaClass.canonicalName}:onMeasure---mWidth=$mWidth,mHeight=$mHeight")
|
||||||
|
if (isNeedMeasure) {
|
||||||
|
mCentreX = mWidth / 2f
|
||||||
|
mCentreY = mHeight / 2f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow()
|
||||||
|
LogUtil.e("onAttachedToWindow,${javaClass.canonicalName}视图显示")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
LogUtil.e("${javaClass.canonicalName}------>onDraw")
|
||||||
|
super.onDraw(canvas)
|
||||||
|
|
||||||
|
canvas.apply {
|
||||||
|
save()
|
||||||
|
//只为旋转才移动坐标轴
|
||||||
|
translate(mCentreX, mCentreY)
|
||||||
|
rotate(mRotateAngle.toFloat())
|
||||||
|
startDraw(this)
|
||||||
|
restore()
|
||||||
|
// val a = getRectABCDXYPoint(FencesRectView.POINT_A)
|
||||||
|
// drawCircle(a[0], a[1], 50f, mTextPaint)
|
||||||
|
// val b = getRectABCDXYPoint(FencesRectView.POINT_B)
|
||||||
|
// drawCircle(b[0], b[1], 50f, mTextPaint)
|
||||||
|
// val c = getRectABCDXYPoint(FencesRectView.POINT_C)
|
||||||
|
// drawCircle(c[0], c[1], 50f, mTextPaint)
|
||||||
|
// val d = getRectABCDXYPoint(FencesRectView.POINT_D)
|
||||||
|
// drawCircle(d[0], d[1], 50f, mTextPaint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startDraw(canvas: Canvas) {
|
||||||
|
LogUtil.e("startDraw:mRotateAngle=$mRotateAngle")
|
||||||
|
|
||||||
|
if (mRectWidth == 0f && mRectHeight == 0f) return
|
||||||
|
|
||||||
|
mRectLeft = -mRectWidth / 2
|
||||||
|
mRectTop = -mRectHeight / 2
|
||||||
|
mRectRight = mRectLeft + mRectWidth
|
||||||
|
mRectBottom = mRectTop + mRectHeight
|
||||||
|
|
||||||
|
mLocationBitmap?.apply {
|
||||||
|
val locationImageHalfWidth = width / 2f
|
||||||
|
val locationImageHalfHeight = height / 2f
|
||||||
|
val left = -locationImageHalfWidth
|
||||||
|
val top = -locationImageHalfHeight
|
||||||
|
canvas.drawBitmap(this, left, top, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
mRectF.apply {
|
||||||
|
left = mRectLeft
|
||||||
|
top = mRectTop
|
||||||
|
right = mRectRight
|
||||||
|
bottom = mRectBottom
|
||||||
|
}
|
||||||
|
canvas.drawRect(mRectF, mFillPaint)
|
||||||
|
canvas.drawRect(mRectF, mStrokePaint)
|
||||||
|
|
||||||
|
mDotBitmap?.apply {
|
||||||
|
val dotImageHalfWidth = width / 2f
|
||||||
|
val dotImageHalfHeight = height / 2f
|
||||||
|
canvas.drawBitmap(
|
||||||
|
this, mRectLeft - dotImageHalfWidth, mRectTop - dotImageHalfHeight, null
|
||||||
|
)
|
||||||
|
canvas.drawBitmap(
|
||||||
|
this, mRectRight - dotImageHalfWidth, mRectTop - dotImageHalfHeight, null
|
||||||
|
)
|
||||||
|
canvas.drawBitmap(
|
||||||
|
this, mRectLeft - dotImageHalfWidth, mRectBottom - dotImageHalfHeight, null
|
||||||
|
)
|
||||||
|
if (!isShowDistance) {
|
||||||
|
canvas.drawBitmap(
|
||||||
|
this, mRectRight - dotImageHalfWidth, mRectBottom - dotImageHalfHeight, null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (isShowDistance) {
|
||||||
|
|
||||||
|
mRotateBitmap?.apply {
|
||||||
|
val startX = mRectLeft + mRectWidth / 2
|
||||||
|
canvas.drawLine(
|
||||||
|
startX,
|
||||||
|
mRectTop,
|
||||||
|
startX,
|
||||||
|
mRectTop - QMUIDisplayHelper.dpToPx(3).toFloat(),
|
||||||
|
mStrokePaint
|
||||||
|
)
|
||||||
|
canvas.drawBitmap(
|
||||||
|
this,
|
||||||
|
startX - width / 2,
|
||||||
|
mRectTop - height - QMUIDisplayHelper.dpToPx(2).toFloat(),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
mStartRotateAngle = atan2(mRectTop - height, startX - width / 2) / Math.PI * 180
|
||||||
|
}
|
||||||
|
mZoomBitmap?.apply {
|
||||||
|
canvas.drawBitmap(
|
||||||
|
this, mRectRight - width / 2, mRectBottom - height / 2, null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
canvasDistanceText(canvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canvasDistanceText(canvas: Canvas) {
|
||||||
|
val fontMetrics = mTextPaint.fontMetrics
|
||||||
|
if (!TextUtils.isEmpty(mRectWidthDistanceText)) {
|
||||||
|
//文字宽度
|
||||||
|
val textWidth = mTextPaint.measureText(mRectWidthDistanceText)
|
||||||
|
//文字高度
|
||||||
|
val textHeight = fontMetrics.bottom - fontMetrics.top
|
||||||
|
|
||||||
|
val y = if (mHeight / 2 - mRectHeight / 2 > textHeight) {
|
||||||
|
mRectHeight / 2 + textHeight
|
||||||
|
} else {
|
||||||
|
mHeight / 2 - textHeight / 6
|
||||||
|
}
|
||||||
|
canvas.drawText(
|
||||||
|
mRectWidthDistanceText, -(textWidth / 2), y, mTextPaint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(mRectHeightDistanceText)) {
|
||||||
|
//文字宽度
|
||||||
|
val textWidth = mTextPaint.measureText(mRectHeightDistanceText)
|
||||||
|
//文字高度
|
||||||
|
val textHeight = fontMetrics.bottom - fontMetrics.top
|
||||||
|
|
||||||
|
val x = if (mWidth / 2 - mRectWidth / 2 > textWidth) {
|
||||||
|
mRectWidth / 2 + (textHeight / 6)
|
||||||
|
} else {
|
||||||
|
mWidth / 2 - textWidth - textHeight / 6
|
||||||
|
}
|
||||||
|
canvas.drawText(
|
||||||
|
mRectHeightDistanceText, x, (textHeight / 2), mTextPaint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnRectViewRotateScaleClickListener(onRectViewRotateScaleClickListener: OnRectViewRotateScaleClickListener) {
|
||||||
|
mRotateScaleClickListener = onRectViewRotateScaleClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置默认长宽大小,初始化使用
|
||||||
|
*/
|
||||||
|
fun initDefaultWidthHeight(width: Int = 150, height: Int = 150) {
|
||||||
|
if (width <= 0 && height <= 0) return
|
||||||
|
mRectWidth = QMUIDisplayHelper.dpToPx(width).toFloat()
|
||||||
|
mRectHeight = QMUIDisplayHelper.dpToPx(height).toFloat()
|
||||||
|
mRotateAngle = 0.0
|
||||||
|
postInvalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置显示的距离
|
||||||
|
*/
|
||||||
|
fun setShowDisDistance(widthDistance: String, heightDistance: String) {
|
||||||
|
mRectWidthDistanceText = widthDistance
|
||||||
|
mRectHeightDistanceText = heightDistance
|
||||||
|
postInvalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置隐藏显示距离文字
|
||||||
|
*/
|
||||||
|
fun setShowHideDisDistance(show: Boolean) {
|
||||||
|
if (isShowDistance != show) {
|
||||||
|
isShowDistance = show
|
||||||
|
postInvalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置边框颜色和图片
|
||||||
|
*/
|
||||||
|
fun setColorAndImage(
|
||||||
|
context: Context,
|
||||||
|
dashedColorRes: Int = 0,
|
||||||
|
fillBgColorRes: Int = 0,
|
||||||
|
locationDrawableRes: Int = 0,
|
||||||
|
zoomDrawableRes: Int = 0,
|
||||||
|
dotDrawableRes: Int = 0,
|
||||||
|
rotateDrawableRes: Int = 0,disTextColorRes: Int = 0
|
||||||
|
) {
|
||||||
|
var isRefresh = false
|
||||||
|
if (dashedColorRes > 0) {
|
||||||
|
mDashedColor = ContextCompat.getColor(context, dashedColorRes)
|
||||||
|
mStrokePaint.color = mDashedColor
|
||||||
|
isRefresh = true
|
||||||
|
}
|
||||||
|
if (fillBgColorRes > 0) {
|
||||||
|
mFillBgColor = ContextCompat.getColor(context, fillBgColorRes)
|
||||||
|
mFillPaint.color = mFillBgColor
|
||||||
|
isRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationDrawableRes > 0) {
|
||||||
|
mLocationBitmap = ImageUtil.getBitmapFromDrawableAndSvg(context, locationDrawableRes)
|
||||||
|
isRefresh = true
|
||||||
|
}
|
||||||
|
if (zoomDrawableRes > 0) {
|
||||||
|
mZoomBitmap = ImageUtil.getBitmapFromDrawableAndSvg(context, zoomDrawableRes)
|
||||||
|
isRefresh = true
|
||||||
|
}
|
||||||
|
if (rotateDrawableRes > 0) {
|
||||||
|
mRotateBitmap = ImageUtil.getBitmapFromDrawableAndSvg(context, rotateDrawableRes)
|
||||||
|
isRefresh = true
|
||||||
|
}
|
||||||
|
if (dotDrawableRes > 0) {
|
||||||
|
mDotBitmap = ImageUtil.getBitmapFromDrawableAndSvg(context, dotDrawableRes)
|
||||||
|
isRefresh = true
|
||||||
|
}
|
||||||
|
if (disTextColorRes > 0) {
|
||||||
|
mTextPaint.color = ContextCompat.getColor(context, disTextColorRes)
|
||||||
|
isRefresh = true
|
||||||
|
}
|
||||||
|
if (isRefresh) invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCentreX() = mCentreX
|
||||||
|
private fun getCentreY() = mCentreY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取中心点
|
||||||
|
*/
|
||||||
|
fun getRectCentrePoint() = Point(mCentreX.toInt(), mCentreY.toInt())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取矩形4个点的某一个点
|
||||||
|
*/
|
||||||
|
fun getRectABCDPoint(wherePoint: Int): Point {
|
||||||
|
val points = getRectABCDXYPoint(wherePoint)
|
||||||
|
return Point(points[0].toInt(), points[1].toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回矩形第哪个点(A,B,C,D)的坐标
|
||||||
|
* A **** B
|
||||||
|
* D **** C
|
||||||
|
*/
|
||||||
|
private fun getRectABCDXYPoint(wherePoint: Int): FloatArray {
|
||||||
|
when (wherePoint) {
|
||||||
|
POINT_A -> {
|
||||||
|
val leftTop = getRotateXY(mRectLeft, mRectTop, mRotateAngle)
|
||||||
|
return floatArrayOf(
|
||||||
|
leftTop[0] + mCentreX, leftTop[1] + mCentreY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
POINT_B -> {
|
||||||
|
val rightTop = getRotateXY(mRectRight, mRectTop, mRotateAngle)
|
||||||
|
return floatArrayOf(
|
||||||
|
rightTop[0] + mCentreX, rightTop[1] + mCentreY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
POINT_D -> {
|
||||||
|
val leftBottom = getRotateXY(mRectLeft, mRectBottom, mRotateAngle)
|
||||||
|
return floatArrayOf(
|
||||||
|
leftBottom[0] + mCentreX, leftBottom[1] + mCentreY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
POINT_C -> {
|
||||||
|
val rightBottom = getRotateXY(mRectRight, mRectBottom, mRotateAngle)
|
||||||
|
return floatArrayOf(
|
||||||
|
rightBottom[0] + mCentreX, rightBottom[1] + mCentreY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return floatArrayOf(
|
||||||
|
0f, 0f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||||
|
|
||||||
|
when (event?.action) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
val x = event.x
|
||||||
|
val y = event.y
|
||||||
|
LogUtil.e("action_down,x=$x,y=$y")
|
||||||
|
mRotateBitmap?.apply {
|
||||||
|
LogUtil.e("x2=${width / 2f + mCentreX},x1=${-width / 2f + mCentreX}")
|
||||||
|
LogUtil.e("y2=${-(mRectHeight / 2 + height) + mCentreY},aaa=${-mRectHeight / 2 + mCentreY}")
|
||||||
|
|
||||||
|
if (isClickRotate(x, y, mRotateAngle)) {
|
||||||
|
LogUtil.e("点击了旋转")
|
||||||
|
isRotate = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//是否按下了放大缩小按钮
|
||||||
|
mZoomBitmap?.apply {
|
||||||
|
if (!isRotate && isClickScale(x, y, mRotateAngle)) {
|
||||||
|
isScale = true
|
||||||
|
LogUtil.e("点击了放大缩小")
|
||||||
|
mOldX = x
|
||||||
|
mOldY = y
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//因为有旋转角度,需要减去中心
|
||||||
|
if (mRectF.contains(x - mCentreX, y - mCentreY)) {
|
||||||
|
isDrag = true
|
||||||
|
mOldX = x
|
||||||
|
mOldY = y
|
||||||
|
mStrokePaint.strokeWidth = QMUIDisplayHelper.dpToPx(3).toFloat()
|
||||||
|
mRotateScaleClickListener?.rectStartDragClick()
|
||||||
|
invalidate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_MOVE -> {
|
||||||
|
val x = event.x
|
||||||
|
val y = event.y
|
||||||
|
LogUtil.e("action_move,x=$x,y=$y,oldX=$mOldX,oldY=$mOldY")
|
||||||
|
if (isRotate) {
|
||||||
|
//计算手指按下画布移动后的x,y角度
|
||||||
|
val transformAngle = atan2(y - mCentreY, x - mCentreX) / Math.PI * 180
|
||||||
|
//计算需要旋转的角度
|
||||||
|
mRotateAngle = transformAngle - mStartRotateAngle
|
||||||
|
LogUtil.e("mRotateAngle=$mRotateAngle")
|
||||||
|
invalidate()
|
||||||
|
return true
|
||||||
|
} else if (isScale) {
|
||||||
|
LogUtil.e("mRectWidth=$mRectWidth,mRectHeight=$mRectHeight,x - oldX=${x - mOldX},y - oldY=${y - mOldY}")
|
||||||
|
val transformX = x - mOldX
|
||||||
|
val transformY = y - mOldY
|
||||||
|
val newRectWidth = mRectWidth + transformX
|
||||||
|
val newRectHeight = mRectHeight + transformY
|
||||||
|
//限制缩放不能太小
|
||||||
|
if (mLimitSide == 0f) {
|
||||||
|
if (newRectWidth < mLocationBitmap!!.height || newRectHeight < mLocationBitmap!!.height) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if (newRectWidth < mLimitSide || newRectHeight < mLimitSide) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
mRotateScaleClickListener?.rectScalingClick()
|
||||||
|
mRectWidth = newRectWidth
|
||||||
|
mRectHeight = newRectHeight
|
||||||
|
mOldX = x
|
||||||
|
mOldY = y
|
||||||
|
invalidate()
|
||||||
|
return true
|
||||||
|
} else if (isDrag) {
|
||||||
|
val transformX = x - mOldX
|
||||||
|
val transformY = y - mOldY
|
||||||
|
mCentreX += transformX
|
||||||
|
mCentreY += transformY
|
||||||
|
mOldX = x
|
||||||
|
mOldY = y
|
||||||
|
invalidate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_UP -> {
|
||||||
|
if (isRotate) {
|
||||||
|
mRotateScaleClickListener?.rectRotateClick()
|
||||||
|
isRotate = false
|
||||||
|
}
|
||||||
|
if (isScale) {
|
||||||
|
mRotateScaleClickListener?.rectScaleEndClick()
|
||||||
|
isScale = false
|
||||||
|
}
|
||||||
|
if (isDrag) {
|
||||||
|
mRotateScaleClickListener?.rectEndDragClick()
|
||||||
|
isDrag = false
|
||||||
|
mStrokePaint.strokeWidth = QMUIDisplayHelper.dpToPx(1).toFloat()
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onTouchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x 是坐标轴移动以(mCentreX,mCentreY)原点的坐标
|
||||||
|
* @param y
|
||||||
|
* 获取以mCentreX、mCentreY为原点的坐标轴进行旋转后的XY坐标
|
||||||
|
*/
|
||||||
|
private fun getRotateXY(x: Float, y: Float, rotate: Double): FloatArray {
|
||||||
|
//Math.toRadians(double angdeg) 角度转化为弧度
|
||||||
|
//Math.toDegrees(Math.PI/2);弧度转化为角度 (π/2的角度值)
|
||||||
|
val curAngle = atan2(y, x) / Math.PI * 180
|
||||||
|
val sumAngle = curAngle + rotate
|
||||||
|
//将角度化为弧度
|
||||||
|
// val angle = Math.PI / 180 * sumAngle
|
||||||
|
val angle = Math.toRadians(sumAngle)
|
||||||
|
//初始坐标与中点形成的直线长度不管怎么旋转都是不会变的,用勾股定理求出然后将其作为斜边
|
||||||
|
val c = sqrt(x.pow(2) + y.pow(2))
|
||||||
|
//斜边乘sin值等于即可求出y坐标
|
||||||
|
val a = sin(angle) * c
|
||||||
|
//斜边乘cos值等于即可求出x坐标
|
||||||
|
val b = cos(angle) * c
|
||||||
|
//目前的xy坐标是相对于图片中点为原点的坐标轴
|
||||||
|
return floatArrayOf((b).toFloat(), (a).toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否点击按下了旋转,按照移动了坐标轴原点(mCentreX,mCentreY)计算初始坐标
|
||||||
|
*/
|
||||||
|
private fun isClickRotate(clickX: Float, clickY: Float, rotate: Double): Boolean {
|
||||||
|
var x1 = -(mRectWidth / 2 - mRotateBitmap!!.width / 2)
|
||||||
|
var x2 = (mRectWidth / 2 - mRotateBitmap!!.width / 2)
|
||||||
|
var y1 = -(mRectHeight / 2 + mRotateBitmap!!.height + mRotateBitmap!!.width)
|
||||||
|
var y2 = -(mRectHeight / 2 - mRotateBitmap!!.height)
|
||||||
|
val minX = getRotateXY(x1, y1, rotate)
|
||||||
|
val maxX = getRotateXY(x2, y2, rotate)
|
||||||
|
x1 = minX[0]
|
||||||
|
x2 = maxX[0]
|
||||||
|
if (minX[0] > maxX[0]) {
|
||||||
|
x1 = maxX[0]
|
||||||
|
x2 = minX[0]
|
||||||
|
}
|
||||||
|
y1 = minX[1]
|
||||||
|
y2 = maxX[1]
|
||||||
|
if (minX[1] > maxX[1]) {
|
||||||
|
y1 = maxX[1]
|
||||||
|
y2 = minX[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理旋转到某个角度时,旋转图片按钮会超出坐标范围内,不在可点击坐标范围内,把可点击的坐标范围扩大
|
||||||
|
if (abs(x2 - x1) < mRotateBitmap!!.height) {
|
||||||
|
if (x2 > 0) {
|
||||||
|
x2 += mRotateBitmap!!.height
|
||||||
|
} else {
|
||||||
|
x1 -= mRotateBitmap!!.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (abs(y2 - y1) < mRotateBitmap!!.height) {
|
||||||
|
if (y2 > 0) {
|
||||||
|
y2 += mRotateBitmap!!.height
|
||||||
|
} else {
|
||||||
|
y1 -= mRotateBitmap!!.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val transformX = clickX - mCentreX
|
||||||
|
val transformY = clickY - mCentreY
|
||||||
|
|
||||||
|
if (transformX in x1..x2 && transformY in y1..y2) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否点击按下了放大缩小,按照移动了坐标轴原点(mCentreX,mCentreY)计算初始坐标
|
||||||
|
*/
|
||||||
|
private fun isClickScale(clickX: Float, clickY: Float, rotate: Double): Boolean {
|
||||||
|
var x1 = (mRectWidth / 2 - mZoomBitmap!!.width)
|
||||||
|
var x2 = (mRectWidth / 2 + mZoomBitmap!!.width)
|
||||||
|
var y1 = (mRectHeight / 2 - mRotateBitmap!!.height)
|
||||||
|
var y2 = (mRectHeight / 2 + mRotateBitmap!!.height)
|
||||||
|
val minX = getRotateXY(x1, y1, rotate)
|
||||||
|
val maxX = getRotateXY(x2, y2, rotate)
|
||||||
|
x1 = minX[0]
|
||||||
|
x2 = maxX[0]
|
||||||
|
if (minX[0] > maxX[0]) {
|
||||||
|
x1 = maxX[0]
|
||||||
|
x2 = minX[0]
|
||||||
|
}
|
||||||
|
y1 = minX[1]
|
||||||
|
y2 = maxX[1]
|
||||||
|
if (minX[1] > maxX[1]) {
|
||||||
|
y1 = maxX[1]
|
||||||
|
y2 = minX[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
val transformX = clickX - mCentreX
|
||||||
|
val transformY = clickY - mCentreY
|
||||||
|
|
||||||
|
if (transformX in x1..x2 && transformY in y1..y2) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置显示编辑的矩形(利用假设方式)
|
||||||
|
* 错误方式
|
||||||
|
fun setRectABCDXYPoint(left: Float, top: Float, right: Float, bottom: Float) {
|
||||||
|
//假设A没有旋转的,原点在矩形中心,正常与x轴的角度135℃
|
||||||
|
val noRotateAngle = 135.0
|
||||||
|
val rotateAX = left - mCentreX
|
||||||
|
val rotateAY = top - mCentreY
|
||||||
|
//计算现在编辑后的矩形A点的角度
|
||||||
|
val curAngle = atan2(rotateAY, rotateAX) / Math.PI * 180
|
||||||
|
//计算旋转了多少度
|
||||||
|
mRotateAngle = noRotateAngle - curAngle
|
||||||
|
//初始坐标与矩形中点形成的直线长度不管怎么旋转都是不会变的,用勾股定理求出然后将其作为斜边
|
||||||
|
val c = sqrt(rotateAX.pow(2) + rotateAY.pow(2))
|
||||||
|
//将角度化为弧度,计算旋转后的与x轴的角度,然后算出长和宽
|
||||||
|
val angle = Math.toRadians(curAngle)
|
||||||
|
//斜边乘sin值等于即可求出y坐标
|
||||||
|
val a = sin(angle) * c
|
||||||
|
//斜边乘cos值等于即可求出x坐标
|
||||||
|
val b = cos(angle) * c
|
||||||
|
mRectLeft = (-abs(b)).toFloat()
|
||||||
|
mRectTop = (-abs(a)).toFloat()
|
||||||
|
|
||||||
|
// mRectWidth = abs(b * 2).toFloat()
|
||||||
|
mRectWidth = abs(right) - abs(left)
|
||||||
|
// mRectHeight = abs(a * 2).toFloat()
|
||||||
|
mRectHeight = abs(bottom) - abs(top)
|
||||||
|
|
||||||
|
mRectRight = mRectWidth / 2
|
||||||
|
mRectBottom = mRectHeight / 2
|
||||||
|
|
||||||
|
isEdit = true
|
||||||
|
|
||||||
|
LogUtil.e("编辑的坐标($mRectLeft,$mRectTop),($mRectRight,$mRectBottom),($mCentreX,$mCentreY)")
|
||||||
|
postInvalidate()
|
||||||
|
} */
|
||||||
|
|
||||||
|
fun setRectABCDXYPoint(
|
||||||
|
pointCentre: Point, pointA: Point, pointB: Point, pointC: Point, pointD: Point
|
||||||
|
) {
|
||||||
|
mCentreX = pointCentre.x.toFloat()
|
||||||
|
mCentreY = pointCentre.y.toFloat()
|
||||||
|
//利用屏幕的坐标系根据勾股定理算出矩形的边长,A点和D点与坐标轴组成一个三角形
|
||||||
|
mRectHeight = sqrt(
|
||||||
|
(abs(pointA.x - pointD.x).toFloat()).pow(2) + (abs(pointD.y - pointA.y).toFloat()).pow(2)
|
||||||
|
)
|
||||||
|
//A点和B点与坐标轴组成一个三角形
|
||||||
|
mRectWidth = sqrt(
|
||||||
|
(abs(pointB.x - pointA.x).toFloat()).pow(2) + (abs(pointB.y - pointA.y).toFloat()).pow(2)
|
||||||
|
)
|
||||||
|
|
||||||
|
val rotateAX = pointA.x - mCentreX
|
||||||
|
val rotateAY = pointA.y - mCentreY
|
||||||
|
//计算现在编辑后的矩形A点与x轴形成的角度
|
||||||
|
val curAngle = atan2(rotateAY, rotateAX) / Math.PI * 180
|
||||||
|
|
||||||
|
//初始坐标与矩形中点形成的直线长度不管怎么旋转都是不会变的,用勾股定理求出然后将其作为斜边
|
||||||
|
// val c = sqrt(rotateAX.pow(2) + rotateAY.pow(2))
|
||||||
|
//将角度化为弧度,计算旋转后的与x轴的角度,然后算出长和宽
|
||||||
|
// val angle = Math.toRadians(curAngle)
|
||||||
|
//斜边乘sin值等于即可求出y坐标
|
||||||
|
// val a = sin(angle) * c
|
||||||
|
//斜边乘cos值等于即可求出x坐标
|
||||||
|
// val b = cos(angle) * c
|
||||||
|
|
||||||
|
//根据矩形长宽算出没有旋转的矩形坐标
|
||||||
|
val rectLeft = -mRectWidth / 2
|
||||||
|
val rectTop = -mRectHeight / 2
|
||||||
|
val rectRight = rectLeft + mRectWidth
|
||||||
|
val rectBottom = rectTop + mRectHeight
|
||||||
|
//计算A没有旋转的,原点在矩形中心,正常与x轴的角度
|
||||||
|
val noRotateAngle = atan2(rectTop, rectLeft) / Math.PI * 180
|
||||||
|
//计算旋转了多少度
|
||||||
|
mRotateAngle = curAngle - noRotateAngle
|
||||||
|
LogUtil.e("没有旋转时初始角度=$noRotateAngle,当前旋转后的角度curAngle=$curAngle,旋转了多少度mRotateAngle=$mRotateAngle")
|
||||||
|
LogUtil.e("编辑的坐标($rectLeft,$rectTop),($rectRight,$rectBottom),($mCentreX,$mCentreY)")
|
||||||
|
postInvalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setCentreXY(point: Point) {
|
||||||
|
point.apply {
|
||||||
|
if (mCentreX != x.toFloat() || mCentreY != y.toFloat()) {
|
||||||
|
mCentreX = x.toFloat()
|
||||||
|
mCentreY = y.toFloat()
|
||||||
|
isNeedMeasure = false
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromWindow() {
|
||||||
|
super.onDetachedFromWindow()
|
||||||
|
LogUtil.e("onDetachedFromWindow,${javaClass.canonicalName}视图销毁")
|
||||||
|
if (null != mDotBitmap) {
|
||||||
|
mDotBitmap?.recycle()
|
||||||
|
mDotBitmap = null
|
||||||
|
}
|
||||||
|
if (null != mLocationBitmap) {
|
||||||
|
mLocationBitmap?.recycle()
|
||||||
|
mLocationBitmap = null
|
||||||
|
}
|
||||||
|
if (null != mRotateBitmap) {
|
||||||
|
mRotateBitmap?.recycle()
|
||||||
|
mRotateBitmap = null
|
||||||
|
}
|
||||||
|
if (null != mZoomBitmap) {
|
||||||
|
mZoomBitmap?.recycle()
|
||||||
|
mZoomBitmap = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -172,12 +172,12 @@ class FencesRectView3 : View {
|
|||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
|
|
||||||
canvas.apply {
|
canvas.apply {
|
||||||
save()
|
// save()
|
||||||
//只为旋转才移动坐标轴
|
// //只为旋转才移动坐标轴
|
||||||
translate(mCentreX, mCentreY)
|
// translate(mCentreX, mCentreY)
|
||||||
rotate(mRotateAngle.toFloat())
|
// rotate(mRotateAngle.toFloat())
|
||||||
startDraw(this)
|
startDraw(this)
|
||||||
restore()
|
// restore()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -867,11 +867,7 @@
|
|||||||
<string name="txt_residual_value">Restwert</string>
|
<string name="txt_residual_value">Restwert</string>
|
||||||
<string name="txt_month_unit">/Monat x%s</string>
|
<string name="txt_month_unit">/Monat x%s</string>
|
||||||
<string name="txt_auto_subscription_year">(Verlängerung zu $%s jährlich danach)</string>
|
<string name="txt_auto_subscription_year">(Verlängerung zu $%s jährlich danach)</string>
|
||||||
<string name="txt_auto_subscription_year1">(Verlängerung zu</string>
|
|
||||||
<string name="txt_auto_subscription_year2">jährlich danach)</string>
|
|
||||||
<string name="txt_auto_subscription_month">(Verlängerung zu $%s monatlich danach)</string>
|
<string name="txt_auto_subscription_month">(Verlängerung zu $%s monatlich danach)</string>
|
||||||
<string name="txt_auto_subscription_month1">(Verlängerung zu</string>
|
|
||||||
<string name="txt_auto_subscription_month2">monatlich danach)</string>
|
|
||||||
<string name="txt_about_dec">Haustiere orten und trainieren, um Verlust/Gefahren vorzubeugen.</string>
|
<string name="txt_about_dec">Haustiere orten und trainieren, um Verlust/Gefahren vorzubeugen.</string>
|
||||||
<string name="txt_year_unit">/%s Jahr</string>
|
<string name="txt_year_unit">/%s Jahr</string>
|
||||||
<string name="txt_advanced_set">Erweiterte Einstellungen</string>
|
<string name="txt_advanced_set">Erweiterte Einstellungen</string>
|
||||||
|
|||||||
@@ -910,11 +910,7 @@
|
|||||||
<string name="txt_residual_value">剩余价值</string>
|
<string name="txt_residual_value">剩余价值</string>
|
||||||
<string name="txt_month_unit">/月 x%s</string>
|
<string name="txt_month_unit">/月 x%s</string>
|
||||||
<string name="txt_auto_subscription_year">(此后按每年 $%s 续订)</string>
|
<string name="txt_auto_subscription_year">(此后按每年 $%s 续订)</string>
|
||||||
<string name="txt_auto_subscription_year1">(此后按</string>
|
|
||||||
<string name="txt_auto_subscription_year2">/年续费)</string>
|
|
||||||
<string name="txt_auto_subscription_month">(此后按每月 $%s 续订)</string>
|
<string name="txt_auto_subscription_month">(此后按每月 $%s 续订)</string>
|
||||||
<string name="txt_auto_subscription_month1">(此后按</string>
|
|
||||||
<string name="txt_auto_subscription_month2">/月续费)</string>
|
|
||||||
<string name="txt_about_dec">追踪并训练宠物,以防丢失或危险。</string>
|
<string name="txt_about_dec">追踪并训练宠物,以防丢失或危险。</string>
|
||||||
<string name="txt_year_unit">/%s 年</string>
|
<string name="txt_year_unit">/%s 年</string>
|
||||||
<string name="txt_advanced_set">高级设置</string>
|
<string name="txt_advanced_set">高级设置</string>
|
||||||
|
|||||||
@@ -968,11 +968,7 @@
|
|||||||
<string name="txt_residual_value">Residual Value</string>
|
<string name="txt_residual_value">Residual Value</string>
|
||||||
<string name="txt_month_unit">/month x%s</string>
|
<string name="txt_month_unit">/month x%s</string>
|
||||||
<string name="txt_auto_subscription_year">(Renew at $%s per year thereafter)</string>
|
<string name="txt_auto_subscription_year">(Renew at $%s per year thereafter)</string>
|
||||||
<string name="txt_auto_subscription_year1">(Renew at </string>
|
|
||||||
<string name="txt_auto_subscription_year2">per year thereafter)</string>
|
|
||||||
<string name="txt_auto_subscription_month">(Renew at $%s per month thereafter)</string>
|
<string name="txt_auto_subscription_month">(Renew at $%s per month thereafter)</string>
|
||||||
<string name="txt_auto_subscription_month1">(Renew at </string>
|
|
||||||
<string name="txt_auto_subscription_month2">per month thereafter)</string>
|
|
||||||
<string name="txt_about_dec">Track and train pets to prevent loss or danger.</string>
|
<string name="txt_about_dec">Track and train pets to prevent loss or danger.</string>
|
||||||
<string name="txt_year_unit">/%s year</string>
|
<string name="txt_year_unit">/%s year</string>
|
||||||
<string name="txt_advanced_set">Advanced Setting</string>
|
<string name="txt_advanced_set">Advanced Setting</string>
|
||||||
@@ -1067,7 +1063,10 @@
|
|||||||
<string name="txt_renewal_day">Renewal: $%s/%s day on %s</string>
|
<string name="txt_renewal_day">Renewal: $%s/%s day on %s</string>
|
||||||
<string name="txt_day_unit">/day x%s</string>
|
<string name="txt_day_unit">/day x%s</string>
|
||||||
<string name="txt_location_tip">"ABBIDOT APP collects location data,The route and distance between the current location and the device can be calculated."</string>
|
<string name="txt_location_tip">"ABBIDOT APP collects location data,The route and distance between the current location and the device can be calculated."</string>
|
||||||
<string name="txt_auto_subscription_day">(Renew at $%s per %s day thereafter)</string>
|
<string name="txt_auto_subscription_day">(Renew at $%s per day thereafter)</string>
|
||||||
|
<string name="txt_auto_subscription_days">(Renew at $%s/%s days thereafter)</string>
|
||||||
<string name="txt_locating">Locating…</string>
|
<string name="txt_locating">Locating…</string>
|
||||||
|
<string name="txt_auto_subscription_years">(Renew at $%s/%s years thereafter)</string>
|
||||||
|
<string name="txt_auto_subscription_months">(Renew at $%s/%s months thereafter)</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user