From c161f90b0ed2ce7ff696da51a711cffd575788ad Mon Sep 17 00:00:00 2001 From: yezhiqiu <123456@qq.com> Date: Tue, 13 Oct 2026 18:12:04 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9F=A9=E5=BD=A2=E5=9B=B4=E6=A0=8F=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=AF=8F=E4=B8=AA=E8=A7=92=E9=83=BD=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E6=8B=96=E5=8A=A8=EF=BC=8C=E4=B8=94=E5=AF=B9=E8=A7=92?= =?UTF-8?q?=E5=9B=BA=E5=AE=9A=E4=B8=8D=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 4 +- .../AddAndEditFencesZoneBaseActivity.kt | 2 +- .../ui/common/map/FencesAddEditMapCommon.kt | 4 +- .../tracker/ui/fragment/map/MapV3Fragment.kt | 10 +- .../baidumap/FencesAddEditBaiduMapFragment.kt | 6 +- .../FencesAddEditGoogleMapFragment.kt | 6 +- .../abbidot/tracker/widget/FencesRectView.kt | 2 +- .../abbidot/tracker/widget/FencesRectView2.kt | 432 ++++++++++++------ .../layout/activity_add_edit_fences_zone.xml | 2 +- 9 files changed, 304 insertions(+), 164 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 787e0aa..1abee76 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,8 +30,8 @@ android { targetSdkVersion 35 versionCode 2601 // versionName "2.6.1" - versionName "2.6.1-Beta2" -4 + versionName "2.6.1-Beta3" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ndk { diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/device/fences/AddAndEditFencesZoneBaseActivity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/device/fences/AddAndEditFencesZoneBaseActivity.kt index 2ad9dc4..afb1cd6 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/device/fences/AddAndEditFencesZoneBaseActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/device/fences/AddAndEditFencesZoneBaseActivity.kt @@ -618,7 +618,7 @@ open class AddAndEditFencesZoneBaseActivity : R.color.red_color3, R.drawable.icon_fences_zone_gps_red_svg, R.drawable.icon_fence_rect_zoom_red_svg, - R.drawable.icon_fence_dot_red_svg, + R.drawable.icon_fence_rect_zoom_red, R.drawable.icon_fence_rect_red_rotate_svg ) fencesZonePolygonView.setColorAndImage( diff --git a/app/src/main/java/com/abbidot/tracker/ui/common/map/FencesAddEditMapCommon.kt b/app/src/main/java/com/abbidot/tracker/ui/common/map/FencesAddEditMapCommon.kt index 399f220..c332afc 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/common/map/FencesAddEditMapCommon.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/common/map/FencesAddEditMapCommon.kt @@ -14,7 +14,7 @@ import com.abbidot.tracker.util.LonAndLatUtil import com.abbidot.tracker.vm.GeoCoderViewModel import com.abbidot.tracker.widget.FencesCircleView import com.abbidot.tracker.widget.FencesPolygonView -import com.abbidot.tracker.widget.FencesRectView +import com.abbidot.tracker.widget.FencesRectView2 import com.google.android.gms.maps.model.LatLng /** @@ -30,7 +30,7 @@ class FencesAddEditMapCommon : BaseMapCommon() { context: Context, fencesBean: FencesBean, fencesCircleView: FencesCircleView, - fencesRectView: FencesRectView, + fencesRectView: FencesRectView2, fencesPolygonView: FencesPolygonView, isEdit: Boolean, onActivityCallBack: OnActivityCallBack?, diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/MapV3Fragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/MapV3Fragment.kt index 63aa215..89db25a 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/MapV3Fragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/MapV3Fragment.kt @@ -823,7 +823,15 @@ class MapV3Fragment : BaseFragment(FragmentMapV3Binding::i isMoveCamera = false } mHomeMapCommon.startRefreshUserLocation() - mHomeMapCommon.updateOtherPetLatLng(otherPets) + getHomeV2Activity()?.let { m -> + m.getPet(false)?.let { p -> + //删除当前选中宠物 + otherPets?.let { o -> + mHomeMapCommon.updateOtherPetLatLng(o.filter { it.petId != p.petId } + .toMutableList()) + } + } + } if (isShowFence) { fences?.let { fences -> diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/baidumap/FencesAddEditBaiduMapFragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/baidumap/FencesAddEditBaiduMapFragment.kt index 5fa3dff..883c134 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/baidumap/FencesAddEditBaiduMapFragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/baidumap/FencesAddEditBaiduMapFragment.kt @@ -13,7 +13,7 @@ import com.abbidot.tracker.util.LonAndLatUtil import com.abbidot.tracker.util.Util import com.abbidot.tracker.widget.FencesCircleView import com.abbidot.tracker.widget.FencesPolygonView -import com.abbidot.tracker.widget.FencesRectView +import com.abbidot.tracker.widget.FencesRectView2 import com.baidu.location.BDLocation import com.baidu.mapapi.map.BaiduMap import com.baidu.mapapi.map.MapStatus @@ -31,7 +31,7 @@ class FencesAddEditBaiduMapFragment : BaseBaiduMapFragment() { private lateinit var mFencesBean: FencesBean private var isEditFences = false private lateinit var mFencesCircleView: FencesCircleView - private lateinit var mFencesRectView: FencesRectView + private lateinit var mFencesRectView: FencesRectView2 private lateinit var mFencesPolygonView: FencesPolygonView private var mOnActivityCallBack: FencesAddEditMapCommon.OnActivityCallBack? = null @@ -45,7 +45,7 @@ class FencesAddEditBaiduMapFragment : BaseBaiduMapFragment() { context: Context, fencesBean: FencesBean, fencesCircleView: FencesCircleView, - fencesRectView: FencesRectView, + fencesRectView: FencesRectView2, fencesPolygonView: FencesPolygonView, isEdit: Boolean, onActivityCallBack: FencesAddEditMapCommon.OnActivityCallBack?, diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/FencesAddEditGoogleMapFragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/FencesAddEditGoogleMapFragment.kt index 359668d..bc67ee5 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/FencesAddEditGoogleMapFragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/FencesAddEditGoogleMapFragment.kt @@ -12,7 +12,7 @@ import com.abbidot.tracker.ui.common.map.FencesAddEditMapCommon import com.abbidot.tracker.util.Util import com.abbidot.tracker.widget.FencesCircleView import com.abbidot.tracker.widget.FencesPolygonView -import com.abbidot.tracker.widget.FencesRectView +import com.abbidot.tracker.widget.FencesRectView2 import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.model.LatLng import kotlin.math.abs @@ -28,7 +28,7 @@ class FencesAddEditGoogleMapFragment : BaseGoogleMapFragment() { private lateinit var mFencesBean: FencesBean private var isEditFences = false private lateinit var mFencesCircleView: FencesCircleView - private lateinit var mFencesRectView: FencesRectView + private lateinit var mFencesRectView: FencesRectView2 private lateinit var mFencesPolygonView: FencesPolygonView private var mOnActivityCallBack: FencesAddEditMapCommon.OnActivityCallBack? = null @@ -46,7 +46,7 @@ class FencesAddEditGoogleMapFragment : BaseGoogleMapFragment() { context: Context, fencesBean: FencesBean, fencesCircleView: FencesCircleView, - fencesRectView: FencesRectView, + fencesRectView: FencesRectView2, fencesPolygonView: FencesPolygonView, isEdit: Boolean, onActivityCallBack: FencesAddEditMapCommon.OnActivityCallBack?, diff --git a/app/src/main/java/com/abbidot/tracker/widget/FencesRectView.kt b/app/src/main/java/com/abbidot/tracker/widget/FencesRectView.kt index 152a689..dc581a3 100644 --- a/app/src/main/java/com/abbidot/tracker/widget/FencesRectView.kt +++ b/app/src/main/java/com/abbidot/tracker/widget/FencesRectView.kt @@ -32,7 +32,7 @@ import kotlin.math.sqrt /** *Created by .yzq on 2022/1/22/022. * @link - * @description:添加围栏矩形,只针对围栏 + * @description:添加围栏矩形,只针对围栏,固定一个角拖动,其他角会跟随拖动 * * 按以下点组成的矩形 * A B diff --git a/app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt b/app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt index 12925e8..91ea01d 100644 --- a/app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt +++ b/app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt @@ -25,6 +25,7 @@ import com.qmuiteam.qmui.util.QMUIDisplayHelper import kotlin.math.abs import kotlin.math.atan2 import kotlin.math.cos +import kotlin.math.max import kotlin.math.pow import kotlin.math.sin import kotlin.math.sqrt @@ -32,7 +33,7 @@ import kotlin.math.sqrt /** *Created by .yzq on 2022/1/22/022. * @link - * @description:添加围栏矩形,只针对围栏 + * @description:添加围栏矩形,只针对围栏,拖动哪个角就哪个角就变化,其他角会保持不动 * * 按以下点组成的矩形 * A B @@ -63,8 +64,8 @@ class FencesRectView2 : View { //定位图标 private var mLocationBitmap: Bitmap? = null - private var mDotBitmap: Bitmap? = null - private var mZoomBitmap: Bitmap? = null + private var mZoomBitmap1: Bitmap? = null + private var mZoomBitmap2: Bitmap? = null private var mRotateBitmap: Bitmap? = null private var mDashedColor = 0 @@ -76,6 +77,23 @@ class FencesRectView2 : View { private var mOldX = 0f private var mOldY = 0f + //是否正在拖动4个角中的一个 + private var isCornerDrag = false + + //当前拖动的是哪个角(POINT_A/B/C/D) + private var mCornerDragPoint = 0 + + //拖动时保持不动的对角屏幕坐标 + private var mFixedCornerX = 0f + private var mFixedCornerY = 0f + + //本手势是否已触碰过最小尺寸,保证每次手势只弹一次提示 + private var mHitMinThisGesture = false + + //按下时localX/localY的正负号(1或-1),拖动中变号说明越过了对角,钉住不动 + private var mInitialLocalXSign = 0 + private var mInitialLocalYSign = 0 + //是否显示距离 private var isShowDistance = true private var mRectWidthDistanceText = "" @@ -137,11 +155,12 @@ class FencesRectView2 : View { 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 = + 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) @@ -175,14 +194,6 @@ class FencesRectView2 : View { 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) } } @@ -213,25 +224,6 @@ class FencesRectView2 : View { 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) { @@ -250,12 +242,22 @@ class FencesRectView2 : View { mRectTop - height - QMUIDisplayHelper.dpToPx(2).toFloat(), null ) - mStartRotateAngle = atan2(mRectTop - height, startX - width / 2) / Math.PI * 180 } - mZoomBitmap?.apply { + 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) @@ -339,9 +341,10 @@ class FencesRectView2 : View { dashedColorRes: Int = 0, fillBgColorRes: Int = 0, locationDrawableRes: Int = 0, - zoomDrawableRes: Int = 0, - dotDrawableRes: Int = 0, - rotateDrawableRes: Int = 0,disTextColorRes: Int = 0 + zoom1DrawableRes: Int = 0, + zoom2DrawableRes: Int = 0, + rotateDrawableRes: Int = 0, + disTextColorRes: Int = 0 ) { var isRefresh = false if (dashedColorRes > 0) { @@ -359,18 +362,18 @@ class FencesRectView2 : View { mLocationBitmap = ImageUtil.getBitmapFromDrawableAndSvg(context, locationDrawableRes) isRefresh = true } - if (zoomDrawableRes > 0) { - mZoomBitmap = ImageUtil.getBitmapFromDrawableAndSvg(context, zoomDrawableRes) + if (zoom1DrawableRes > 0) { + mZoomBitmap1 = ImageUtil.getBitmapFromDrawableAndSvg(context, zoom1DrawableRes) + isRefresh = true + } + if (zoom2DrawableRes > 0) { + mZoomBitmap2 = ImageUtil.getBitmapFromDrawableAndSvg(context, zoom2DrawableRes) 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 @@ -442,28 +445,38 @@ class FencesRectView2 : View { val x = event.x val y = event.y LogUtil.e("action_down,x=$x,y=$y") + //1. 旋转按钮(优先,旋转状态下禁止其他手势) 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("点击了旋转") + //按下瞬间记录起始角度 = 当前旋转的等效起始角度,这样MOVE里 transformAngle - mStartRotateAngle 保留已有旋转并叠加增量 + mStartRotateAngle = + atan2(y - mCentreY, x - mCentreX) / Math.PI * 180 - mRotateAngle isRotate = true return true } } - //是否按下了放大缩小按钮 - mZoomBitmap?.apply { - if (!isRotate && isClickScale(x, y, mRotateAngle)) { - isScale = true - LogUtil.e("点击了放大缩小") - mOldX = x - mOldY = y - return true - } + //2. 矩形的4个角点,按下后拖角、对角不动(优先于角点上的放大按钮,因为放大按钮功能已被角点拖覆盖) + if (isClickCorner(x, y)) { + LogUtil.e("点击了角点:$mCornerDragPoint") + isCornerDrag = true + return true } + //3. 放大缩小按钮(作为后备,在非角点区域) +// mZoomBitmap1?.apply { +// if (isClickScale(x, y, mRotateAngle)) { +// isScale = true +// LogUtil.e("点击了放大缩小") +// mOldX = x +// mOldY = y +// return true +// } +// } - //因为有旋转角度,需要减去中心 + //4. 整体拖动(需要在矩形区域内,且旋转时判断不准,可接受) if (mRectF.contains(x - mCentreX, y - mCentreY)) { isDrag = true mOldX = x @@ -487,26 +500,34 @@ class FencesRectView2 : View { 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() + } +// 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) { +// isHasLimitSide = true +// return true +// } +// } else if (newRectWidth < mLimitSide || newRectHeight < mLimitSide) { +// isHasLimitSide = true +// mRotateScaleClickListener?.rectMinSizeClick() +// return true +// } +// mRotateScaleClickListener?.rectScalingClick() +// mRectWidth = newRectWidth +// mRectHeight = newRectHeight +// mOldX = x +// mOldY = y +// invalidate() +// return true +// } + else if (isCornerDrag) { + moveCorner(x, y) return true } else if (isDrag) { val transformX = x - mOldX @@ -521,25 +542,42 @@ class FencesRectView2 : View { } 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() - } + handleActionUpOrCancel() + } + + MotionEvent.ACTION_CANCEL -> { + handleActionUpOrCancel() } } return super.onTouchEvent(event) } + /** + * ACTION_UP 和 ACTION_CANCEL 共用的收尾:重置所有手势状态 + * ACTION_CANCEL 在父 View(如百度地图)拦截触摸时触发,必须正确清理 + */ + private fun handleActionUpOrCancel() { + mHitMinThisGesture = false + if (isRotate) { + mRotateScaleClickListener?.rectRotateClick() + isRotate = false + } + if (isScale) { + mRotateScaleClickListener?.rectScaleEndClick() + isScale = false + } + if (isCornerDrag) { + mRotateScaleClickListener?.rectScaleEndClick() + isCornerDrag = false + } + if (isDrag) { + mRotateScaleClickListener?.rectEndDragClick() + isDrag = false + mStrokePaint.strokeWidth = QMUIDisplayHelper.dpToPx(1).toFloat() + invalidate() + } + } + /** * @param x 是坐标轴移动以(mCentreX,mCentreY)原点的坐标 * @param y @@ -563,14 +601,19 @@ class FencesRectView2 : View { 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) + private fun isClick( + clickX: Float, + clickY: Float, + xX1: Float, + xX2: Float, + yY1: Float, + yY2: Float, + rotate: Double + ): Boolean { + var x1 = xX1 + var x2 = xX2 + var y1 = yY1 + var y2 = yY2 val minX = getRotateXY(x1, y1, rotate) val maxX = getRotateXY(x2, y2, rotate) x1 = minX[0] @@ -586,63 +629,161 @@ class FencesRectView2 : View { 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 padding = QMUIDisplayHelper.dpToPx(8).toFloat() + x1 -= padding + x2 += padding + y1 -= padding + y2 += padding val transformX = clickX - mCentreX val transformY = clickY - mCentreY - if (transformX in x1..x2 && transformY in y1..y2) { - return true - } - return false + return transformX in x1..x2 && transformY in y1..y2 + } + + /** + * 是否点击按下了旋转,按照移动了坐标轴原点(mCentreX,mCentreY)计算初始坐标 + * 按钮绘制在矩形上边中点正上方,点击范围紧贴按钮自身大小 + */ + private fun isClickRotate(clickX: Float, clickY: Float, rotate: Double): Boolean { + val halfW = mRotateBitmap!!.width / 2f + val halfH = mRotateBitmap!!.height / 2f + //按钮中心在本地坐标的位置(以矩形中心为原点,未旋转) + val btnCx = 0f + val btnCy = -(mRectHeight / 2f + halfH + QMUIDisplayHelper.dpToPx(2).toFloat()) + val x1 = btnCx - halfW + val x2 = btnCx + halfW + val y1 = btnCy - halfH + val y2 = btnCy + halfH + return isClick(clickX, clickY, x1, x2, y1, y2, rotate) } /** * 是否点击按下了放大缩小,按照移动了坐标轴原点(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 x1 = (mRectWidth / 2 - mZoomBitmap1!!.width / 2) + val x2 = (mRectWidth / 2 + mZoomBitmap1!!.width / 2) + val y1 = (mRectHeight / 2 - mZoomBitmap1!!.height) + val y2 = (mRectHeight / 2 + mZoomBitmap1!!.height) + return isClick(clickX, clickY, x1, x2, y1, y2, rotate) + } + /** + * 是否按下了矩形的4个角中的一个,按下时记录当前拖动的角和需要保持不动的对角坐标 + */ + private fun isClickCorner(clickX: Float, clickY: Float): Boolean { + if (mRectWidth == 0f && mRectHeight == 0f) return false + //因为有旋转角度,先把点击的坐标逆旋转回矩形本地坐标系(以矩形中心为原点) + val angle = Math.toRadians(mRotateAngle) + val cosAngle = cos(angle).toFloat() + val sinAngle = sin(angle).toFloat() val transformX = clickX - mCentreX val transformY = clickY - mCentreY - - if (transformX in x1..x2 && transformY in y1..y2) { - return true + val localX = transformX * cosAngle + transformY * sinAngle + val localY = -transformX * sinAngle + transformY * cosAngle + for (corner in intArrayOf(POINT_A, POINT_B, POINT_C, POINT_D)) { + val cornerXY = getRectLocalCornerXY(corner) + val offsetX = localX - cornerXY[0] + val offsetY = localY - cornerXY[1] + if (sqrt(offsetX.pow(2) + offsetY.pow(2)) <= getCornerTouchRadius()) { + mCornerDragPoint = corner + //记录需要保持不动的对角(屏幕坐标) + val fixedXY = getRectABCDXYPoint(getOppositePoint(corner)) + mFixedCornerX = fixedXY[0] + mFixedCornerY = fixedXY[1] + //记录角点在本地坐标系中的正负号,拖动中变号说明越过了对角 + mInitialLocalXSign = if (cornerXY[0] >= 0f) 1 else -1 + mInitialLocalYSign = if (cornerXY[1] >= 0f) 1 else -1 + return true + } } return false } + /** + * 角点的触摸范围(圆形半径),基于实际绘制在角点的按钮尺寸,外加手指容差 + */ + private fun getCornerTouchRadius(): Float { + val cornerIconHalf = listOfNotNull(mZoomBitmap1, mZoomBitmap2) + .maxOfOrNull { max(it.width, it.height) / 2f } ?: 0f + return cornerIconHalf + QMUIDisplayHelper.dpToPx(10) + } + + /** + * 获取矩形某个角在本地坐标系(以矩形中心为原点,未旋转)的坐标 + */ + private fun getRectLocalCornerXY(wherePoint: Int): FloatArray { + val halfWidth = mRectWidth / 2f + val halfHeight = mRectHeight / 2f + return when (wherePoint) { + POINT_A -> floatArrayOf(-halfWidth, -halfHeight) + POINT_B -> floatArrayOf(halfWidth, -halfHeight) + POINT_D -> floatArrayOf(-halfWidth, halfHeight) + else -> floatArrayOf(halfWidth, halfHeight)//POINT_C + } + } + + /** + * 获取对角的点,A对C,B对D + */ + private fun getOppositePoint(wherePoint: Int): Int { + return when (wherePoint) { + POINT_A -> POINT_C + POINT_B -> POINT_D + POINT_C -> POINT_A + POINT_D -> POINT_B + else -> wherePoint + } + } + + /** + * 拖动角点,对角保持不动,只改变当前拖动角的位置 + * 对角指向手指的向量投影到矩形的宽高方向得到新的宽高,中心点取对角与拖动角连线的中点 + */ + private fun moveCorner(moveX: Float, moveY: Float) { + //保持不动的对角指向手指位置的向量 + val vectorX = moveX - mFixedCornerX + val vectorY = moveY - mFixedCornerY + val angle = Math.toRadians(mRotateAngle) + val cosAngle = cos(angle).toFloat() + val sinAngle = sin(angle).toFloat() + //把向量逆旋转回矩形本地坐标系,得到在宽、高方向上的投影 + val localX = vectorX * cosAngle + vectorY * sinAngle + val localY = -vectorX * sinAngle + vectorY * cosAngle + //拖动中localX/localY变号说明手指已越过对角,钉住不动防止闪跳 + val xSignFlipped = mInitialLocalXSign != 0 && (localX > 0f) != (mInitialLocalXSign > 0) + val ySignFlipped = mInitialLocalYSign != 0 && (localY > 0f) != (mInitialLocalYSign > 0) + //限制边长不能小于最小值,达到最小值后钉住不动,不允许翻过对角 + val minSide = + if (mLimitSide == 0f) (mLocationBitmap?.height ?: 0).toFloat() else mLimitSide + if (xSignFlipped || ySignFlipped || + (minSide > 0f && (abs(localX) < minSide || abs(localY) < minSide)) + ) { + if (!mHitMinThisGesture) { + mHitMinThisGesture = true + mRotateScaleClickListener?.rectMinSizeClick() + } + //已是最小值或越过对角,直接返回保持当前状态,不更新矩形 + return + } + //脱离最小值,清标志 + if (mHitMinThisGesture) { + mHitMinThisGesture = false + } + //限制后的本地坐标再转回屏幕坐标系 + val newVectorX = localX * cosAngle - localY * sinAngle + val newVectorY = localX * sinAngle + localY * cosAngle + mRectWidth = abs(localX) + mRectHeight = abs(localY) + //中心点=对角与拖动角连线的中点,保证对角的位置不会动 + mCentreX = mFixedCornerX + newVectorX / 2f + mCentreY = mFixedCornerY + newVectorY / 2f + mRotateScaleClickListener?.rectScalingClick() + invalidate() + } + /** * 设置显示编辑的矩形(利用假设方式) * 错误方式 @@ -736,22 +877,13 @@ class FencesRectView2 : View { 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 + listOf(mLocationBitmap, mRotateBitmap, mZoomBitmap1, mZoomBitmap2).forEach { bm -> + if (bm != null && !bm.isRecycled) bm.recycle() } + mLocationBitmap = null + mRotateBitmap = null + mZoomBitmap1 = null + mZoomBitmap2 = null } } diff --git a/app/src/main/res/layout/activity_add_edit_fences_zone.xml b/app/src/main/res/layout/activity_add_edit_fences_zone.xml index ad4af98..0ed9552 100644 --- a/app/src/main/res/layout/activity_add_edit_fences_zone.xml +++ b/app/src/main/res/layout/activity_add_edit_fences_zone.xml @@ -31,7 +31,7 @@ app:fences_circle_dashed_color="@color/select_color" app:fences_circle_fill_bg_color="@color/select_color4" /> -