1.完成Nearby 显示定位策略

2.多边形围栏距离限制
This commit is contained in:
yezhiqiu
2026-03-02 15:33:05 +08:00
parent f0dcad40ea
commit 1aa6f7a247
12 changed files with 948 additions and 8 deletions

View File

@@ -28,9 +28,9 @@ android {
applicationId "com.abbidot.tracker"
minSdkVersion 23
targetSdkVersion 35
versionCode 2108
// versionName "2.1.8"
versionName "2.1.8-Beta1"
versionCode 2109
// versionName "2.1.9"
versionName "2.1.9-Beta1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -43,11 +43,13 @@ import com.abbidot.tracker.ui.activity.map.LiveActivityV3
import com.abbidot.tracker.ui.common.map.HomeMapCommonV3
import com.abbidot.tracker.util.Util
import com.abbidot.tracker.util.ViewUtil
import com.abbidot.tracker.util.bluetooth.SRBleCmdUtil
import com.abbidot.tracker.util.bluetooth.SRBleUtil
import com.abbidot.tracker.vm.CountDownTimerViewModel
import com.abbidot.tracker.vm.FencesMapViewModel
import com.abbidot.tracker.vm.MapViewModel
import com.clj.fastble.BleManager
import com.google.android.gms.maps.model.LatLng
import com.hjq.permissions.XXPermissions
import com.hjq.permissions.permission.PermissionLists
import dagger.hilt.android.AndroidEntryPoint
@@ -243,7 +245,6 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
mCountDownTimerViewModel.mCountDownEndLiveData.observe(this) {
mCountDownTimerViewModel.isStartCountDown = true
LogUtil.e("22222222222")
mViewBinding.llHomeMapTopPet.homeDataPetNameSmall.text =
getHomeV2Activity()?.getPet()?.petName
}
@@ -291,6 +292,22 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
if (macID == reportData.mac) updateBleReportData(reportData)
}
}
//设备获取手机位置
XEventBus.observe(this, EventName.BleGetLocation) { mac: String ->
getHomeV2Activity()?.getPet(false)?.apply {
if (macID == mac) {
mHomeMapCommon.getUserGoogleLatLng()?.let { latLng: LatLng ->
SRBleUtil.instance.getConnectMacDevice(mac)?.let { ble ->
SRBleUtil.instance.writeData(
ble.bleDevice, SRBleCmdUtil.instance.getPhoneLocation(
latLng.latitude, latLng.longitude
)
)
}
}
}
}
}
//接收蓝牙连接状态
XEventBus.observe(this, EventName.ConnectDeviceState) { trackBle: BleTrackDeviceBean ->
getHomeV2Activity()?.getPet(false)?.apply {

View File

@@ -193,6 +193,7 @@ class FencesAddEditGoogleMapFragment : BaseGoogleMapFragment() {
LogUtil.e("setScaleLimitDistance,$stepDistance,$limitRadius,$limitSide,${cameraPosition.zoom}")
mFencesCircleView.mLimitRadius = limitRadius.toFloat()
mFencesRectView.mLimitSide = limitSide.toFloat()
mFencesPolygonView.mLimitSide = limitSide.toFloat()
}
}

View File

@@ -367,5 +367,12 @@ class SRBleCmdUtil private constructor() {
byteArray = byteArray.plus(longTo2ByteArray(duration))
return getCrc8Cmd(byteArray)
}
fun getPhoneLocation(lat: Double, lon: Double): ByteArray {
var byteArray = byteArrayOf(0x23, CMD_READ.toByte(), 0)
byteArray = byteArray.plus(longTo4ByteArray((lon * 1000000).toLong()))
byteArray = byteArray.plus(longTo4ByteArray((lat * 1000000).toLong()))
return getCrc8Cmd(byteArray)
}
}

View File

@@ -226,6 +226,9 @@ class SRBleUtil private constructor() {
openBleReportNotify(
bleDevice, notifyServiceUUID, bleNotifyCharacteristicUUID
)
} else if (data0 == 0x23 && data1 == 0x00) {
LogUtil.e("${bleDevice.mac},获取手机定位")
XEventBus.post(EventName.BleGetLocation, bleDevice.mac)
} else {
val deviceData = ReceiveDeviceData(bleDevice, data, bleDevice.mac)
XEventBus.post(EventName.DeviceReceiveData, deviceData)

View File

@@ -60,6 +60,9 @@ class FencesPolygonView : View {
private var mOutHideRectWidth = 0f
private var mOutHideRectHeight = 0f
//限制边长
var mLimitSide = 0f
//定位图标
private var mLocationBitmap: Bitmap? = null
private var mDotBitmap: Bitmap? = null
@@ -334,7 +337,12 @@ class FencesPolygonView : View {
if (intersectTFEF) {
return false
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
return false
}
mOutHideRectWidth = tempRect.width()
mOutHideRectHeight = tempRect.height()
mAPoint.x = x
mAPoint.y = y
isInvalidate = true
@@ -391,6 +399,12 @@ class FencesPolygonView : View {
if (intersectTAFA) {
return false
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
return false
}
mOutHideRectWidth = tempRect.width()
mOutHideRectHeight = tempRect.height()
mFPoint.x = x
mFPoint.y = y
isInvalidate = true
@@ -446,6 +460,12 @@ class FencesPolygonView : View {
if (intersectTDED) {
return false
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
return false
}
mOutHideRectWidth = tempRect.width()
mOutHideRectHeight = tempRect.height()
mEPoint.x = x
mEPoint.y = y
isInvalidate = true
@@ -501,6 +521,12 @@ class FencesPolygonView : View {
if (intersectTEFE) {
return false
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
return false
}
mOutHideRectWidth = tempRect.width()
mOutHideRectHeight = tempRect.height()
mDPoint.x = x
mDPoint.y = y
isInvalidate = true
@@ -556,6 +582,12 @@ class FencesPolygonView : View {
if (intersectTFAF) {
return false
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
return false
}
mOutHideRectWidth = tempRect.width()
mOutHideRectHeight = tempRect.height()
mCPoint.x = x
mCPoint.y = y
isInvalidate = true
@@ -611,6 +643,12 @@ class FencesPolygonView : View {
if (intersectTAAB) {
return false
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
return false
}
mOutHideRectWidth = tempRect.width()
mOutHideRectHeight = tempRect.height()
mBPoint.x = x
mBPoint.y = y
isInvalidate = true
@@ -657,12 +695,65 @@ class FencesPolygonView : View {
return super.onTouchEvent(event)
}
private fun getTempRect(touchPointTag: String, tempPoint: Point) {
private fun getTempRect(touchPointTag: String, tempPoint: PointBean): RectF {
when (touchPointTag) {
mTageA->{
mTageA -> {
val listX =
listOf(tempPoint.x, mBPoint.x, mCPoint.x, mDPoint.x, mEPoint.x, mFPoint.x)
val listY =
listOf(tempPoint.y, mBPoint.y, mCPoint.y, mDPoint.y, mEPoint.y, mFPoint.y)
return getTRect(listX, listY)
}
mTageB -> {
val listX =
listOf(mAPoint.x, tempPoint.x, mCPoint.x, mDPoint.x, mEPoint.x, mFPoint.x)
val listY =
listOf(mAPoint.y, tempPoint.y, mCPoint.y, mDPoint.y, mEPoint.y, mFPoint.y)
return getTRect(listX, listY)
}
mTageC -> {
val listX =
listOf(mAPoint.x, mBPoint.x, tempPoint.x, mDPoint.x, mEPoint.x, mFPoint.x)
val listY =
listOf(mAPoint.y, mBPoint.y, tempPoint.y, mDPoint.y, mEPoint.y, mFPoint.y)
return getTRect(listX, listY)
}
mTageD -> {
val listX =
listOf(mAPoint.x, mBPoint.x, mCPoint.x, tempPoint.x, mEPoint.x, mFPoint.x)
val listY =
listOf(mAPoint.y, mBPoint.y, mCPoint.y, tempPoint.y, mEPoint.y, mFPoint.y)
return getTRect(listX, listY)
}
mTageE -> {
val listX =
listOf(mAPoint.x, mBPoint.x, mCPoint.x, mDPoint.x, tempPoint.x, mFPoint.x)
val listY =
listOf(mAPoint.y, mBPoint.y, mCPoint.y, mDPoint.y, tempPoint.y, mFPoint.y)
return getTRect(listX, listY)
}
mTageF -> {
val listX =
listOf(mAPoint.x, mBPoint.x, mCPoint.x, mDPoint.x, mEPoint.x, tempPoint.x)
val listY =
listOf(mAPoint.y, mBPoint.y, mCPoint.y, mDPoint.y, mEPoint.y, tempPoint.y)
return getTRect(listX, listY)
}
}
return RectF()
}
private fun getTRect(listX: List<Float>, listY: List<Float>): RectF {
val minX = listX.min()
val minY = listY.min()
val maxX = listX.max()
val maxY = listY.max()
return RectF(minX, minY, maxX, maxY)
}
private fun crossProduct(p1: PointBean, p2: PointBean, p3: PointBean): Float {

View File

@@ -0,0 +1,818 @@
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 FencesRectView3 : 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 mZoomBitmap1: Bitmap? = null
private var mZoomBitmap2: 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)
mZoomBitmap1 =
ImageUtil.getBitmapFromDrawableAndSvg(context, R.drawable.icon_fence_rect_zoom_svg)
mZoomBitmap2 =
ImageUtil.getBitmapFromDrawableAndSvg(context, R.drawable.icon_fence_rect_zoom)
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=$isNeedMeasure,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()
}
}
private fun startDraw(canvas: Canvas) {
// if (mRectWidth == 0f && mRectHeight == 0f) return
mRectF.apply {
left = mRectLeft
top = mRectTop
right = mRectRight
bottom = mRectBottom
}
mLocationBitmap?.apply {
canvas.drawBitmap(
this, mRectF.centerX() - width / 2, mRectF.centerY() - height / 2, null
)
}
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
}
mZoomBitmap1?.apply {
canvas.drawBitmap(
this, mRectRight - width / 2, mRectBottom - height / 2, null
)
canvas.drawBitmap(
this, mRectLeft - width / 2, mRectTop - height / 2, null
)
}
mZoomBitmap2?.apply {
canvas.drawBitmap(
this, mRectRight - width / 2, mRectTop - height / 2, null
)
canvas.drawBitmap(
this, mRectLeft - 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
canvas.drawText(
mRectWidthDistanceText,
mRectF.centerX() - textWidth / 2,
mRectBottom + textHeight,
mTextPaint
)
}
if (!TextUtils.isEmpty(mRectHeightDistanceText)) {
//文字宽度
val textWidth = mTextPaint.measureText(mRectHeightDistanceText)
//文字高度
val textHeight = fontMetrics.bottom - fontMetrics.top
canvas.drawText(
mRectHeightDistanceText,
mRectRight + textWidth / 10,
mRectF.centerY() + 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) {
mZoomBitmap1 = 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
)
}
private var mClickTag = 0
@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
}
}
//是否按下了放大缩小按钮
mZoomBitmap1?.apply {
// if (!isRotate && isClickScale(x, y, mRotateAngle)) {
if (!isRotate) {
if (x > mRectF.left - width / 2 && x < mRectF.left + width / 2 && y > mRectF.top - height / 2 && y < mRectF.top + height / 2) {
isScale = true
mClickTag = 1
LogUtil.e("点击了放大缩小")
mOldX = x
mOldY = y
return true
} else if (isClickScale(x, y, mRotateAngle)) {
isScale = true
mClickTag = 3
LogUtil.e("点击了放大缩小")
mOldX = x
mOldY = y
return true
}
}
}
mZoomBitmap2?.apply {
if (!isRotate) {
if (x > mRectF.right - width / 2 && x < mRectF.right + width / 2 && y > mRectF.top - height / 2 && y < mRectF.top + height / 2) {
isScale = true
mClickTag = 2
LogUtil.e("点击了放大缩小")
mOldX = x
mOldY = y
return true
} else if (x > mRectF.left - width / 2 && x < mRectF.left + width / 2 && y > mRectF.bottom - height / 2 && y < mRectF.bottom + height / 2) {
isScale = true
mClickTag = 4
LogUtil.e("点击了放大缩小")
mOldX = x
mOldY = y
return true
}
}
}
//因为有旋转角度,需要减去中心
if (mRectF.contains(x, y)) {
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) {
//计算手指按下画布移动后的xy角度
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
when (mClickTag) {
1 -> {
mRectLeft = x
mRectTop = y
}
2 -> {
mRectRight = x
mRectTop = y
}
3 -> {
mRectRight = x
mRectBottom = y
}
4 -> {
mRectLeft = x
mRectBottom = y
}
}
invalidate()
return true
} else if (isDrag) {
val transformX = x - mOldX
val transformY = y - mOldY
mRectLeft += transformX
mRectTop += transformY
mRectRight += transformX
mRectBottom += 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 是坐标轴移动以mCentreXmCentreY原点的坐标
* @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())
}
/**
* 是否点击按下了旋转按照移动了坐标轴原点mCentreXmCentreY计算初始坐标
*/
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
}
/**
* 是否点击按下了放大缩小,按照移动了坐标轴原点mCentreXmCentreY计算初始坐标
*/
private fun isClickScale(clickX: Float, clickY: Float, rotate: Double): Boolean {
var x1 = (mRectWidth / 2 - mZoomBitmap1!!.width)
var x2 = (mRectWidth / 2 + mZoomBitmap1!!.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
mRectLeft = - mRectWidth / 2
mRectTop = - mRectHeight / 2
mRectRight = mRectWidth / 2
mRectBottom = mRectHeight / 2
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 != mZoomBitmap1) {
mZoomBitmap1?.recycle()
mZoomBitmap1 = null
}
if (null != mZoomBitmap2) {
mZoomBitmap2?.recycle()
mZoomBitmap2 = null
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.activity.account.FencesAddActivity">
tools:context=".deprecated.ui.activity.profile.FencesAddActivity">
<include
android:id="@+id/fences_add_bar"