|
|
@@ -0,0 +1,189 @@
|
|
|
+package com.grkj.iscs.view.widget
|
|
|
+
|
|
|
+import android.content.Context
|
|
|
+import android.graphics.Bitmap
|
|
|
+import android.graphics.BitmapFactory
|
|
|
+import android.graphics.Canvas
|
|
|
+import android.graphics.Color
|
|
|
+import android.graphics.Matrix
|
|
|
+import android.graphics.Paint
|
|
|
+import android.graphics.PointF
|
|
|
+import android.util.Pair
|
|
|
+import android.view.MotionEvent
|
|
|
+import com.grkj.iscs.R
|
|
|
+import com.onlylemi.mapview.library.MapView
|
|
|
+import com.onlylemi.mapview.library.layer.MapBaseLayer
|
|
|
+import kotlin.math.cos
|
|
|
+import kotlin.math.sin
|
|
|
+
|
|
|
+
|
|
|
+class CustomStationLayer @JvmOverloads constructor(
|
|
|
+ mapView: MapView?,
|
|
|
+ private var pointList: List<IsolationPoint> = mutableListOf()
|
|
|
+) : MapBaseLayer(mapView) {
|
|
|
+ private var listener: MarkIsClickListener? = null
|
|
|
+ private var radiusMark = 0f
|
|
|
+ private var isClickMark: Boolean = false
|
|
|
+ private var num: Int
|
|
|
+ private lateinit var paint: Paint
|
|
|
+ private var btnIndex: Int
|
|
|
+ private var currentZoom = 0f
|
|
|
+ private var currentDegree = 0f
|
|
|
+ private lateinit var bgBitmap: Bitmap
|
|
|
+
|
|
|
+
|
|
|
+ init {
|
|
|
+ num = -1
|
|
|
+ btnIndex = -1
|
|
|
+ initLayer()
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun initLayer() {
|
|
|
+ radiusMark = setValue(4.0f)
|
|
|
+ bgBitmap = getResizedBitmapFromMipmap(mapView.context, R.mipmap.red_stroke_bg, 24, 32)
|
|
|
+
|
|
|
+ paint = Paint()
|
|
|
+ paint.isAntiAlias = true
|
|
|
+ paint.style = Paint.Style.FILL_AND_STROKE
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun getResizedBitmapFromMipmap(
|
|
|
+ context: Context,
|
|
|
+ resId: Int,
|
|
|
+ reqWidth: Int,
|
|
|
+ reqHeight: Int
|
|
|
+ ): Bitmap {
|
|
|
+ // First decode with inJustDecodeBounds=true to check dimensions
|
|
|
+ val options = BitmapFactory.Options()
|
|
|
+ options.inJustDecodeBounds = true
|
|
|
+ BitmapFactory.decodeResource(context.resources, resId, options)
|
|
|
+
|
|
|
+ // Calculate inSampleSize
|
|
|
+ options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
|
|
|
+
|
|
|
+ // Decode bitmap with inSampleSize set
|
|
|
+ options.inJustDecodeBounds = false
|
|
|
+ val scaledBitmap = BitmapFactory.decodeResource(context.resources, resId, options)
|
|
|
+
|
|
|
+ // Resize the bitmap if necessary
|
|
|
+ return Bitmap.createScaledBitmap(scaledBitmap, reqWidth, reqHeight, false)
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun calculateInSampleSize(
|
|
|
+ options: BitmapFactory.Options,
|
|
|
+ reqWidth: Int,
|
|
|
+ reqHeight: Int
|
|
|
+ ): Int {
|
|
|
+ // Raw height and width of image
|
|
|
+ val height = options.outHeight
|
|
|
+ val width = options.outWidth
|
|
|
+ var inSampleSize = 1
|
|
|
+
|
|
|
+ if (height > reqHeight || width > reqWidth) {
|
|
|
+ val halfHeight = height / 2
|
|
|
+ val halfWidth = width / 2
|
|
|
+
|
|
|
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
|
|
|
+ // height and width larger than the requested height and width.
|
|
|
+ while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
|
|
|
+ inSampleSize *= 2
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return inSampleSize
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onTouch(event: MotionEvent) {
|
|
|
+ if (pointList.isNotEmpty()) {
|
|
|
+ val goal = mapView.convertMapXYToScreenXY(event.x, event.y)
|
|
|
+
|
|
|
+ for (i in pointList.indices) {
|
|
|
+ var continueOuterLoop = false // 双循环跳出标志变量
|
|
|
+
|
|
|
+ // 计算文字点击
|
|
|
+ val width = paint.measureText(pointList[i].entityName) / currentZoom
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // 计算图标点击
|
|
|
+
|
|
|
+ // 跳出外层循环
|
|
|
+ if (continueOuterLoop) break
|
|
|
+
|
|
|
+ if (i == pointList.size - 1) {
|
|
|
+ isClickMark = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (listener != null && isClickMark) {
|
|
|
+ listener!!.markIsClick(num, btnIndex)
|
|
|
+ mapView.refresh()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun draw(
|
|
|
+ canvas: Canvas,
|
|
|
+ currentMatrix: Matrix,
|
|
|
+ currentZoom: Float,
|
|
|
+ currentRotateDegrees: Float
|
|
|
+ ) {
|
|
|
+ this.currentZoom = currentZoom
|
|
|
+ currentDegree = 360 - currentRotateDegrees
|
|
|
+ if (isVisible) {
|
|
|
+ canvas.save()
|
|
|
+ if (pointList.isNotEmpty()) {
|
|
|
+ pointList.forEach { point ->
|
|
|
+ val mark = point.pos
|
|
|
+ val goal = floatArrayOf(mark.x, mark.y)
|
|
|
+ currentMatrix.mapPoints(goal)
|
|
|
+
|
|
|
+ // 文字背景
|
|
|
+ val width = paint.measureText(point.entityName)
|
|
|
+ paint.style = Paint.Style.FILL
|
|
|
+ paint.strokeWidth = 1.0f
|
|
|
+
|
|
|
+ paint.color = Color.parseColor("#CCFF0000")
|
|
|
+ paint.textSize = radiusMark
|
|
|
+ // 先画背景再画文字防止文字被盖住
|
|
|
+ canvas.drawBitmap(bgBitmap, goal[0] - width / 2, goal[1] - 2 * radiusMark, paint)
|
|
|
+ // 一直显示文字
|
|
|
+ canvas.drawText(
|
|
|
+ point.entityName,
|
|
|
+ goal[0] - width / 2,
|
|
|
+ goal[1] + radiusMark / 3.0f,
|
|
|
+ paint
|
|
|
+ )
|
|
|
+
|
|
|
+ // 定位点,仅调试用,不要显示
|
|
|
+// canvas.drawCircle(goal[0], goal[1], 2f, paint)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ canvas.restore()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun rotatePoint(
|
|
|
+ oriX: Float, oriY: Float, desX: Float, desY: Float, rotateDegrees: Float
|
|
|
+ ): Pair<Float, Float> {
|
|
|
+ // 将度数转换为弧度
|
|
|
+ val theta = Math.toRadians(rotateDegrees.toDouble())
|
|
|
+
|
|
|
+ // 计算旋转后的坐标
|
|
|
+ val newX = (oriX - desX) * cos(theta) - (oriY - desY) * sin(theta) + desX
|
|
|
+ val newY = (oriX - desX) * sin(theta) + ((oriY - desY) * cos(theta)) + desY
|
|
|
+
|
|
|
+ return Pair(newX.toFloat(), newY.toFloat())
|
|
|
+ }
|
|
|
+
|
|
|
+ fun setMarkIsClickListener(listener: MarkIsClickListener?) {
|
|
|
+ this.listener = listener
|
|
|
+ }
|
|
|
+
|
|
|
+ interface MarkIsClickListener {
|
|
|
+ fun markIsClick(index: Int, btnIndex: Int)
|
|
|
+ }
|
|
|
+
|
|
|
+ data class IsolationPoint(val pos: PointF, val entityName: String, val entityId: Long, val isSelected: Boolean)
|
|
|
+}
|