diff --git a/app/build.gradle b/app/build.gradle index c4e780b..1a19847 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,9 +28,9 @@ android { applicationId "com.abbidot.tracker" minSdkVersion 23 targetSdkVersion 35 - versionCode 2109 -// versionName "2.1.9" - versionName "2.1.9-Beta1" + versionCode 2110 +// versionName "2.1.10" + versionName "2.1.10-Beta1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/abbidot/tracker/adapter/MySubscriptionAdapter.kt b/app/src/main/java/com/abbidot/tracker/adapter/MySubscriptionAdapter.kt index 532a182..50d75bd 100644 --- a/app/src/main/java/com/abbidot/tracker/adapter/MySubscriptionAdapter.kt +++ b/app/src/main/java/com/abbidot/tracker/adapter/MySubscriptionAdapter.kt @@ -47,19 +47,34 @@ class MySubscriptionAdapter( holder.setText(R.id.tv_my_subscription_plan_name, item.mealName) holder.getTextView(R.id.tv_my_subscription_auto_subscription_tips).apply { visibility = if (item.subscriptionStatus == ConstantInt.Open) { - text = if (item.mealUnit == ConstantString.PackageUnitYear) String.format( - mContext.getString(R.string.txt_auto_subscription_year), - item.autoRenewPrice.toString() - ) - else if (item.mealUnit == ConstantString.PackageUnitDay) String.format( - mContext.getString(R.string.txt_auto_subscription_day), - "${item.autoRenewPrice}", - "${item.mealPeriod}" - ) - else String.format( - mContext.getString(R.string.txt_auto_subscription_month), - item.autoRenewPrice.toString() - ) + text = when (item.mealUnit) { + ConstantString.PackageUnitYear -> if (item.mealPeriod > 1) String.format( + mContext.getString(R.string.txt_auto_subscription_years), + "${item.autoRenewPrice}", + "${item.mealPeriod}" + ) else String.format( + mContext.getString(R.string.txt_auto_subscription_year), + "${item.autoRenewPrice}" + ) + + 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), + "${item.autoRenewPrice}" + ) + } View.VISIBLE } else View.GONE } diff --git a/app/src/main/java/com/abbidot/tracker/deprecated/ui/activity/data/HistoryDataActivity.kt b/app/src/main/java/com/abbidot/tracker/deprecated/ui/activity/data/HistoryDataActivity.kt index 0479edf..8ee3ec2 100644 --- a/app/src/main/java/com/abbidot/tracker/deprecated/ui/activity/data/HistoryDataActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/deprecated/ui/activity/data/HistoryDataActivity.kt @@ -53,7 +53,7 @@ class HistoryDataActivity : mHistoryDataMapCommon = HistoryDataMapCommon() val fragment = mHistoryDataMapCommon.getMapFragment( - this, MapMarkerInfoView(mContext), mGeoCoderViewModel + this, MapMarkerInfoView(mContext), mGeoCoderViewModel,VerticalTopToBottomSeekBar(mContext) ) {} supportFragmentManager.commit { add(R.id.history_data_map_view, fragment) diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/map/LiveActivityV3.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/map/LiveActivityV3.kt index 73db18a..b33dbc2 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/map/LiveActivityV3.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/map/LiveActivityV3.kt @@ -1088,6 +1088,7 @@ class LiveActivityV3 : BaseActivity(ActivityLiveV3Binding mFindBleDeviceViewModel.stopPlay() showAndHideFindLayout() } else { + mFindBleDeviceViewModel.stopPlay() mFindBleDeviceViewModel.stopFindDevice() finishActivity() } diff --git a/app/src/main/java/com/abbidot/tracker/ui/common/map/HistoryDataMapCommon.kt b/app/src/main/java/com/abbidot/tracker/ui/common/map/HistoryDataMapCommon.kt index fcbf792..17bde2d 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/common/map/HistoryDataMapCommon.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/common/map/HistoryDataMapCommon.kt @@ -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.vm.GeoCoderViewModel import com.abbidot.tracker.widget.MapMarkerInfoView +import com.abbidot.tracker.widget.VerticalTopToBottomSeekBar /** *Created by .yzq on 2022/7/5/005. @@ -25,6 +26,7 @@ class HistoryDataMapCommon : BaseMapCommon() { context: Context, markerInfoView: MapMarkerInfoView, geoCoderViewModel: GeoCoderViewModel, + verticalTopToBottomSeekBar: VerticalTopToBottomSeekBar, mapLoadOk: () -> Unit ): Fragment { return if (AppUtils.isChina(AppUtils.SWITCH_MAP_TYPE)) { @@ -33,7 +35,7 @@ class HistoryDataMapCommon : BaseMapCommon() { mHistoryDataBaiduMapFragment!! } else { mHistoryDataGoogleMapFragment = HistoryDataGoogleMapFragment.newInstance( - context, markerInfoView, geoCoderViewModel, mapLoadOk + context, markerInfoView, geoCoderViewModel, verticalTopToBottomSeekBar, mapLoadOk ) mHistoryDataGoogleMapFragment!! } diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/data/RouteV2Fragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/data/RouteV2Fragment.kt index d9c0744..438bf12 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/data/RouteV2Fragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/data/RouteV2Fragment.kt @@ -85,7 +85,7 @@ class RouteV2Fragment : BaseFragment(FragmentRouteV2Bind root, WindowInsetsCompat.Type.statusBars() ) mFragment = mHistoryDataMapCommon.getMapFragment( - mContext!!, miHomeRouteAddressView, mGeoCoderViewModel + mContext!!, miHomeRouteAddressView, mGeoCoderViewModel, vsbMapRouteLine ) { mapLoadOk() } @@ -188,7 +188,7 @@ class RouteV2Fragment : BaseFragment(FragmentRouteV2Bind when (checkedId) { mViewBinding.rbHistoryRouteMapLine.id -> { MMKVUtil.putInt(MMKVKey.LineSelectPosition, ConstantInt.Line) - mHistoryDataMapCommon.addSetLine(false) + mHistoryDataMapCommon.addSetLine() if (mHistoryDataList.size > 0) { // mHistoryDataMapCommon.slideStopChanged(mHistoryDataList.size - 1) } else { @@ -198,7 +198,7 @@ class RouteV2Fragment : BaseFragment(FragmentRouteV2Bind mViewBinding.rbHistoryRouteMapNumber.id -> { MMKVUtil.putInt(MMKVKey.LineSelectPosition, ConstantInt.Point) - mHistoryDataMapCommon.addSetNumber(false) + mHistoryDataMapCommon.addSetNumber() if (mHistoryDataList.size > 0) { // mHistoryDataMapCommon.slideStopChanged(mHistoryDataList.size - 1) } else { diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/device/HomeTrackFragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/device/HomeTrackFragment.kt index 73f0381..8581c8e 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/device/HomeTrackFragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/device/HomeTrackFragment.kt @@ -301,9 +301,11 @@ class HomeTrackFragment : // mTrackerDFUStateDialog?.addData(this) if (progress == 100) { LogUtil.e("固件下载完成") - mDeviceDFUViewModel.startDFU( - mContext!!, mBleTrackDeviceBean!!.bleDevice!!, filePath - ) + mBleTrackDeviceBean?.let { b -> + b.bleDevice?.let { ble -> + mDeviceDFUViewModel.startDFU(mContext!!, ble, filePath) + } + } } } } @@ -830,7 +832,7 @@ class HomeTrackFragment : mBleTrackDeviceBean?.apply { SRBleUtil.instance.disconnectToMac(mac) } - mMapDeviceBean?.let {ble-> + mMapDeviceBean?.let { ble -> mTrackerSetViewModel.turnOff(ble.deviceServerId) } mTrackMenuList[1].menuValue = "" 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 1f7a8c1..f190563 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 @@ -212,7 +212,6 @@ class FencesAddEditBaiduMapFragment : BaseBaiduMapFragment() { */ fun resetFencesViewCentre(centreLatLng: LatLng) { mBaiduMap?.projection?.toScreenLocation(centreLatLng)?.apply { - LogUtil.e("ddddssddfsdf$this") if (mFencesCircleView.isVisible) { mFencesCircleView.setCentreXY(this) } else if (mFencesRectView.isVisible) { diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/BaseGoogleMapFragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/BaseGoogleMapFragment.kt index f5ba1c0..27241de 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/BaseGoogleMapFragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/BaseGoogleMapFragment.kt @@ -868,16 +868,16 @@ abstract class BaseGoogleMapFragment : ) { //启用“我的位置”图层。 isMyLocationEnabled = true - uiSettings.let { - //禁止显示“我的位置”按钮。 - it.isMyLocationButtonEnabled = false - //用于设置是启用还是停用倾斜手势的偏好设置。 - // https://developers.google.cn/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/UiSettings?hl=zh-cn - it.isTiltGesturesEnabled = false - it.isMapToolbarEnabled = false - it.isTiltGesturesEnabled = false - it.isCompassEnabled = false - } + } + uiSettings.let { + //禁止显示“我的位置”按钮。 + it.isMyLocationButtonEnabled = false + //用于设置是启用还是停用倾斜手势的偏好设置。 + // https://developers.google.cn/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/UiSettings?hl=zh-cn + it.isTiltGesturesEnabled = false + it.isMapToolbarEnabled = false + it.isTiltGesturesEnabled = false + it.isCompassEnabled = false } onMapLoadOk(googleMap) @@ -961,9 +961,9 @@ abstract class BaseGoogleMapFragment : * 由 points 组成的区域 距离手机屏幕距离screenPaddingPx 像素单位 */ fun setLatLngZoom(context: Context, screenPaddingPx: Int, vararg points: LatLng) { - val width = context.resources.displayMetrics.widthPixels - val height = context.resources.displayMetrics.heightPixels - val padding = width / 8 +// val width = context.resources.displayMetrics.widthPixels +// val height = context.resources.displayMetrics.heightPixels +// val padding = width / 8 val builder = LatLngBounds.Builder() for (item in points) { builder.include(item) diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/HistoryDataGoogleMapFragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/HistoryDataGoogleMapFragment.kt index 51431b7..ab3c985 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/HistoryDataGoogleMapFragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/HistoryDataGoogleMapFragment.kt @@ -1,11 +1,8 @@ package com.abbidot.tracker.ui.fragment.map.googlemap -import android.Manifest import android.content.Context -import android.content.pm.PackageManager import android.graphics.BitmapFactory import android.graphics.Typeface -import androidx.core.app.ActivityCompat import androidx.core.view.isVisible import com.abbidot.baselibrary.constant.MMKVKey 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.vm.GeoCoderViewModel 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.model.BitmapDescriptor import com.google.android.gms.maps.model.BitmapDescriptorFactory @@ -34,6 +32,7 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() { // private val mScreenshotsViewModel: ScreenshotsViewModel by viewModels() private lateinit var mGeoCoderViewModel: GeoCoderViewModel private lateinit var mMarkerInfoView: MapMarkerInfoView + private lateinit var mVerticalTopToBottomSeekBar: VerticalTopToBottomSeekBar private val mMarkerOptions = MarkerOptions() private lateinit var mPetIconDescriptor: BitmapDescriptor @@ -56,10 +55,12 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() { context: Context, markerInfoView: MapMarkerInfoView, geoCoderViewModel: GeoCoderViewModel, + verticalTopToBottomSeekBar: VerticalTopToBottomSeekBar, mapLoadOk: () -> Unit ) = HistoryDataGoogleMapFragment().apply { mContext = context mGeoCoderViewModel = geoCoderViewModel + mVerticalTopToBottomSeekBar = verticalTopToBottomSeekBar mMarkerInfoView = markerInfoView mMapLoadOk = mapLoadOk } @@ -100,17 +101,22 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() { if (mMarkerInfoView.isVisible) setMarkerInfoViewOffset(mMarkerInfoView, it) } } - - //检测权限,开启用户的当前位置https://developers.google.cn/maps/documentation/android-sdk/location?hl=zh-cn - if (ActivityCompat.checkSelfPermission( - mContext!!, Manifest.permission.ACCESS_FINE_LOCATION - ) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( - mContext!!, Manifest.permission.ACCESS_COARSE_LOCATION - ) == PackageManager.PERMISSION_GRANTED - ) { - //启用“我的位置”图层。 - isMyLocationEnabled = false + setOnMarkerClickListener { m -> + //点击数字大头针回到该位置 + if (m != mMarker) { + val index = m.tag + if (index is Int) { + mVerticalTopToBottomSeekBar.progress = index + mMarker?.let { + it.position = m.position + } + slideStopChanged(index) + } + true + } else false } + + uiSettings.isCompassEnabled = true } getLastLocation() @@ -267,7 +273,9 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() { ) ) ) - it.addMarker(numberMarker) + it.addMarker(numberMarker)?.apply { + tag = i + } } //设置宠物头像和经纬度 diff --git a/app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt b/app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt new file mode 100644 index 0000000..12925e8 --- /dev/null +++ b/app/src/main/java/com/abbidot/tracker/widget/FencesRectView2.kt @@ -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 + } + } +} + diff --git a/app/src/main/java/com/abbidot/tracker/widget/FencesRectView3.kt b/app/src/main/java/com/abbidot/tracker/widget/FencesRectView3.kt index 896b7a7..f14ed59 100644 --- a/app/src/main/java/com/abbidot/tracker/widget/FencesRectView3.kt +++ b/app/src/main/java/com/abbidot/tracker/widget/FencesRectView3.kt @@ -172,12 +172,12 @@ class FencesRectView3 : View { super.onDraw(canvas) canvas.apply { - save() - //只为旋转才移动坐标轴 - translate(mCentreX, mCentreY) - rotate(mRotateAngle.toFloat()) +// save() +// //只为旋转才移动坐标轴 +// translate(mCentreX, mCentreY) +// rotate(mRotateAngle.toFloat()) startDraw(this) - restore() +// restore() } } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5a0ef5a..9986c75 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -867,11 +867,7 @@ Restwert /Monat x%s (Verlängerung zu $%s jährlich danach) - (Verlängerung zu - jährlich danach) (Verlängerung zu $%s monatlich danach) - (Verlängerung zu - monatlich danach) Haustiere orten und trainieren, um Verlust/Gefahren vorzubeugen. /%s Jahr Erweiterte Einstellungen diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 9181392..65cb00e 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -910,11 +910,7 @@ 剩余价值 /月 x%s (此后按每年 $%s 续订) - (此后按 - /年续费) (此后按每月 $%s 续订) - (此后按 - /月续费) 追踪并训练宠物,以防丢失或危险。 /%s 年 高级设置 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c0ea694..8c0e1ac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -968,11 +968,7 @@ Residual Value /month x%s (Renew at $%s per year thereafter) - (Renew at - per year thereafter) (Renew at $%s per month thereafter) - (Renew at - per month thereafter) Track and train pets to prevent loss or danger. /%s year Advanced Setting @@ -1067,7 +1063,10 @@ Renewal: $%s/%s day on %s /day x%s "ABBIDOT APP collects location data,The route and distance between the current location and the device can be calculated." - (Renew at $%s per %s day thereafter) + (Renew at $%s per day thereafter) + (Renew at $%s/%s days thereafter) Locating… + (Renew at $%s/%s years thereafter) + (Renew at $%s/%s months thereafter) \ No newline at end of file