diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt deleted file mode 100644 index fae29c7..0000000 --- a/app/src/main/java/com/casic/br/operationsite/model/Point.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.casic.br.operationsite.model - -data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt deleted file mode 100644 index fae29c7..0000000 --- a/app/src/main/java/com/casic/br/operationsite/model/Point.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.casic.br.operationsite.model - -data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt index e20f98b..546bcb7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt @@ -187,8 +187,8 @@ binding.rtspPlayerView.startPlayLogic() } - binding.clearVideoRegionButton.setOnClickListener { - binding.regionView.clearRoutePath() + binding.startCheckButton.setOnClickListener { + //打开AI } binding.setVideoRegionButton.setOnClickListener { @@ -310,11 +310,6 @@ binding.rtspPlayerView.backButton.visibility = View.GONE binding.rtspPlayerView.fullscreenButton.visibility = View.GONE - val regionViewParams = binding.regionView.layoutParams as FrameLayout.LayoutParams - regionViewParams.width = videoWidth - regionViewParams.height = videoHeight.toInt() - binding.regionView.layoutParams = regionViewParams - binding.currentSpeedView.text = "速度:${speed}" } diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt deleted file mode 100644 index fae29c7..0000000 --- a/app/src/main/java/com/casic/br/operationsite/model/Point.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.casic.br.operationsite.model - -data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt index e20f98b..546bcb7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt @@ -187,8 +187,8 @@ binding.rtspPlayerView.startPlayLogic() } - binding.clearVideoRegionButton.setOnClickListener { - binding.regionView.clearRoutePath() + binding.startCheckButton.setOnClickListener { + //打开AI } binding.setVideoRegionButton.setOnClickListener { @@ -310,11 +310,6 @@ binding.rtspPlayerView.backButton.visibility = View.GONE binding.rtspPlayerView.fullscreenButton.visibility = View.GONE - val regionViewParams = binding.regionView.layoutParams as FrameLayout.LayoutParams - regionViewParams.width = videoWidth - regionViewParams.height = videoHeight.toInt() - binding.regionView.layoutParams = regionViewParams - binding.currentSpeedView.text = "速度:${speed}" } diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt index 2b55c03..25790db 100644 --- a/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt +++ b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt @@ -1,135 +1,141 @@ package com.casic.br.operationsite.widgets +import android.annotation.SuppressLint import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path +import android.graphics.PointF import android.util.AttributeSet import android.view.MotionEvent import android.view.View -import com.casic.br.operationsite.model.Point +import com.pengxh.kt.lite.extensions.dp2px import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.pow +import kotlin.math.sqrt class VideoRegionView(private val ctx: Context, attrs: AttributeSet) : View(ctx, attrs) { - private val routePath: Path = Path() - private val rectPath: Path = Path() - private val routePaint: Paint = Paint() - private val borderPaint: Paint = Paint() - private var routes = ArrayList() + private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG) + private val vertexPaint = Paint(Paint.ANTI_ALIAS_FLAG) + private val vertices = listOf( + PointF(100f, 100f), // 顶点1 + PointF(300f, 100f), // 顶点2 + PointF(300f, 300f), // 顶点3 + PointF(100f, 300f) // 顶点4 + ) init { - routePaint.isAntiAlias = true - routePaint.color = Color.RED - routePaint.style = Paint.Style.STROKE - routePaint.strokeWidth = 5f //设置线宽 - routePaint.isAntiAlias = true - - borderPaint.isAntiAlias = true - borderPaint.color = Color.BLUE + borderPaint.color = Color.GREEN borderPaint.style = Paint.Style.STROKE - borderPaint.strokeWidth = 5f //设置线宽 + borderPaint.strokeJoin = Paint.Join.ROUND + borderPaint.strokeWidth = 2f.dp2px(ctx) borderPaint.isAntiAlias = true + + vertexPaint.color = Color.RED + vertexPaint.style = Paint.Style.FILL + vertexPaint.isAntiAlias = true + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val width = MeasureSpec.getSize(widthMeasureSpec) + + val desiredHeight = ((9 / 16f) * width).toInt() + + /** + * UNSPECIFIED + * 父容器不对View有任何限制,要多大给多大,这般情况一般用于系统内部,表示一种测量状态,如ScrollView测量子View时用的就是这个。 + * EXACTLY + * 父容器已经检测出View所需要的大小,这个时候View的最终大小就是SpecSize所测定的值,它对应于LayoutParams中的match_parent和具体的数值(如40dp,60dp)这两种模式。 + * AT_MOST + * 父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的wrap_content. + * + * */ + val newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(desiredHeight, MeasureSpec.EXACTLY) + super.onMeasure(widthMeasureSpec, newHeightMeasureSpec) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) - canvas.drawPath(routePath, routePaint) - canvas.drawPath(rectPath, borderPaint) + val path = Path() + path.moveTo(vertices[0].x, vertices[0].y) + for (i in vertices.indices) { + // 绘制四边形 + path.lineTo(vertices[i].x, vertices[i].y) + + //绘制四个顶点 + canvas.drawCircle(vertices[i].x, vertices[i].y, 5f.dp2px(ctx), vertexPaint) + } + path.close() + canvas.drawPath(path, borderPaint) } - override fun onTouchEvent(event: MotionEvent): Boolean { + private var dragVertex: PointF? = null // 当前被拖动的顶点 + private var dragOffsetX = 0f // 拖动偏移量 + private var dragOffsetY = 0f // 拖动偏移量 + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { val x = event.x val y = event.y - routes.add(Point(x, y)) when (event.action) { - MotionEvent.ACTION_DOWN -> routePath.moveTo(x, y) - MotionEvent.ACTION_MOVE -> routePath.lineTo(x, y) - MotionEvent.ACTION_UP -> { - routePath.lineTo(x, y) - - /** - * 找出最大的(x1,y1)和最小的(x2,y2) - * - * 左上(x2,y2) - * 右上(x1,y2) - * 左下(x2,y1) - * 右下(x1,y1) - * */ - val sortedX = routes.sortedBy { point -> point.x } - val sortedY = routes.sortedBy { point -> point.y } - val xMaxPoint = sortedX.last() - val xMinPoint = sortedX.first() - - val yMaxPoint = sortedY.last() - val yMinPoint = sortedY.first() - - /** - * 画出外接矩形 - * */ - val leftTop = Point(xMinPoint.x, yMinPoint.y) - val rightTop = Point(xMaxPoint.x, yMinPoint.y) - val leftBottom = Point(xMinPoint.x, yMaxPoint.y) - val rightBottom = Point(xMaxPoint.x, yMaxPoint.y) - rectPath.moveTo(leftTop.x, leftTop.y) - rectPath.lineTo(rightTop.x, rightTop.y) - rectPath.lineTo(rightBottom.x, rightBottom.y) - rectPath.lineTo(leftBottom.x, leftBottom.y) - rectPath.lineTo(leftTop.x, leftTop.y) - - /** - * 计算出点的相对位置返回给一体机计算 - * */ - val width = ctx.getScreenWidth() - val height = ((9 / 16f) * width).toInt() - - /** - * 区域 - * */ - if (region.isNotEmpty()) { - region.clear() + MotionEvent.ACTION_DOWN -> { + dragVertex = findClosestVertex(x, y) + if (dragVertex != null) { + dragOffsetX = x - dragVertex!!.x + dragOffsetY = y - dragVertex!!.y + return true } - region.add(Point(leftTop.x / width, leftTop.y / height)) - region.add(Point(rightTop.x / width, rightTop.y / height)) - region.add(Point(leftBottom.x / width, leftBottom.y / height)) - region.add(Point(rightBottom.x / width, rightBottom.y / height)) + } - /** - * 点集合 - * */ - if (points.isNotEmpty()) { - points.clear() + MotionEvent.ACTION_MOVE -> { + if (dragVertex != null) { + dragVertex!!.set(x - dragOffsetX, y - dragOffsetY) + invalidate() // 重新绘制视图 + return true } - points.add(floatArrayOf(leftTop.x / width, leftTop.y / height)) - points.add(floatArrayOf(rightTop.x / width, rightTop.y / height)) - points.add(floatArrayOf(rightBottom.x / width, rightBottom.y / height)) - points.add(floatArrayOf(leftBottom.x / width, leftBottom.y / height)) + } + + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + dragVertex = null } } - invalidate() - return true + return super.onTouchEvent(event) } - fun clearRoutePath() { - routePath.reset() - rectPath.reset() - routes.clear() - invalidate() + // 查找最近的顶点 + private fun findClosestVertex(x: Float, y: Float): PointF? { + var closest: PointF? = null + var minDistance = Float.MAX_VALUE + for (vertex in vertices) { + val distance = sqrt( + (x - vertex.x).toDouble().pow(2.0) + (y - vertex.y).toDouble().pow(2.0) + ).toFloat() + if (distance < minDistance) { + minDistance = distance + closest = vertex + } + } + return if (minDistance < 50) { + closest + } else null } - /////////////////////////////////////////////////////////////////////////////////////////////// + fun getConfirmedPoints(): ArrayList { + /** + * 计算出点的相对位置返回给一体机计算 + * */ + val width = ctx.getScreenWidth() + val height = ((9 / 16f) * width).toInt() - private var region = ArrayList() - - fun getConfirmedRegion(): ArrayList = region - - /////////////////////////////////////////////////////////////////////////////////////////////// - - private var points = ArrayList() - - fun getConfirmedPoints(): ArrayList = points + val region = ArrayList() + region.add(floatArrayOf(vertices[0].x / width, vertices[0].y / height)) + region.add(floatArrayOf(vertices[1].x / width, vertices[1].y / height)) + region.add(floatArrayOf(vertices[2].x / width, vertices[2].y / height)) + region.add(floatArrayOf(vertices[3].x / width, vertices[3].y / height)) + return region + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt deleted file mode 100644 index fae29c7..0000000 --- a/app/src/main/java/com/casic/br/operationsite/model/Point.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.casic.br.operationsite.model - -data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt index e20f98b..546bcb7 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/DeviceControlByNativeActivity.kt @@ -187,8 +187,8 @@ binding.rtspPlayerView.startPlayLogic() } - binding.clearVideoRegionButton.setOnClickListener { - binding.regionView.clearRoutePath() + binding.startCheckButton.setOnClickListener { + //打开AI } binding.setVideoRegionButton.setOnClickListener { @@ -310,11 +310,6 @@ binding.rtspPlayerView.backButton.visibility = View.GONE binding.rtspPlayerView.fullscreenButton.visibility = View.GONE - val regionViewParams = binding.regionView.layoutParams as FrameLayout.LayoutParams - regionViewParams.width = videoWidth - regionViewParams.height = videoHeight.toInt() - binding.regionView.layoutParams = regionViewParams - binding.currentSpeedView.text = "速度:${speed}" } diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt index 2b55c03..25790db 100644 --- a/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt +++ b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt @@ -1,135 +1,141 @@ package com.casic.br.operationsite.widgets +import android.annotation.SuppressLint import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path +import android.graphics.PointF import android.util.AttributeSet import android.view.MotionEvent import android.view.View -import com.casic.br.operationsite.model.Point +import com.pengxh.kt.lite.extensions.dp2px import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.pow +import kotlin.math.sqrt class VideoRegionView(private val ctx: Context, attrs: AttributeSet) : View(ctx, attrs) { - private val routePath: Path = Path() - private val rectPath: Path = Path() - private val routePaint: Paint = Paint() - private val borderPaint: Paint = Paint() - private var routes = ArrayList() + private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG) + private val vertexPaint = Paint(Paint.ANTI_ALIAS_FLAG) + private val vertices = listOf( + PointF(100f, 100f), // 顶点1 + PointF(300f, 100f), // 顶点2 + PointF(300f, 300f), // 顶点3 + PointF(100f, 300f) // 顶点4 + ) init { - routePaint.isAntiAlias = true - routePaint.color = Color.RED - routePaint.style = Paint.Style.STROKE - routePaint.strokeWidth = 5f //设置线宽 - routePaint.isAntiAlias = true - - borderPaint.isAntiAlias = true - borderPaint.color = Color.BLUE + borderPaint.color = Color.GREEN borderPaint.style = Paint.Style.STROKE - borderPaint.strokeWidth = 5f //设置线宽 + borderPaint.strokeJoin = Paint.Join.ROUND + borderPaint.strokeWidth = 2f.dp2px(ctx) borderPaint.isAntiAlias = true + + vertexPaint.color = Color.RED + vertexPaint.style = Paint.Style.FILL + vertexPaint.isAntiAlias = true + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val width = MeasureSpec.getSize(widthMeasureSpec) + + val desiredHeight = ((9 / 16f) * width).toInt() + + /** + * UNSPECIFIED + * 父容器不对View有任何限制,要多大给多大,这般情况一般用于系统内部,表示一种测量状态,如ScrollView测量子View时用的就是这个。 + * EXACTLY + * 父容器已经检测出View所需要的大小,这个时候View的最终大小就是SpecSize所测定的值,它对应于LayoutParams中的match_parent和具体的数值(如40dp,60dp)这两种模式。 + * AT_MOST + * 父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的wrap_content. + * + * */ + val newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(desiredHeight, MeasureSpec.EXACTLY) + super.onMeasure(widthMeasureSpec, newHeightMeasureSpec) } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) - canvas.drawPath(routePath, routePaint) - canvas.drawPath(rectPath, borderPaint) + val path = Path() + path.moveTo(vertices[0].x, vertices[0].y) + for (i in vertices.indices) { + // 绘制四边形 + path.lineTo(vertices[i].x, vertices[i].y) + + //绘制四个顶点 + canvas.drawCircle(vertices[i].x, vertices[i].y, 5f.dp2px(ctx), vertexPaint) + } + path.close() + canvas.drawPath(path, borderPaint) } - override fun onTouchEvent(event: MotionEvent): Boolean { + private var dragVertex: PointF? = null // 当前被拖动的顶点 + private var dragOffsetX = 0f // 拖动偏移量 + private var dragOffsetY = 0f // 拖动偏移量 + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { val x = event.x val y = event.y - routes.add(Point(x, y)) when (event.action) { - MotionEvent.ACTION_DOWN -> routePath.moveTo(x, y) - MotionEvent.ACTION_MOVE -> routePath.lineTo(x, y) - MotionEvent.ACTION_UP -> { - routePath.lineTo(x, y) - - /** - * 找出最大的(x1,y1)和最小的(x2,y2) - * - * 左上(x2,y2) - * 右上(x1,y2) - * 左下(x2,y1) - * 右下(x1,y1) - * */ - val sortedX = routes.sortedBy { point -> point.x } - val sortedY = routes.sortedBy { point -> point.y } - val xMaxPoint = sortedX.last() - val xMinPoint = sortedX.first() - - val yMaxPoint = sortedY.last() - val yMinPoint = sortedY.first() - - /** - * 画出外接矩形 - * */ - val leftTop = Point(xMinPoint.x, yMinPoint.y) - val rightTop = Point(xMaxPoint.x, yMinPoint.y) - val leftBottom = Point(xMinPoint.x, yMaxPoint.y) - val rightBottom = Point(xMaxPoint.x, yMaxPoint.y) - rectPath.moveTo(leftTop.x, leftTop.y) - rectPath.lineTo(rightTop.x, rightTop.y) - rectPath.lineTo(rightBottom.x, rightBottom.y) - rectPath.lineTo(leftBottom.x, leftBottom.y) - rectPath.lineTo(leftTop.x, leftTop.y) - - /** - * 计算出点的相对位置返回给一体机计算 - * */ - val width = ctx.getScreenWidth() - val height = ((9 / 16f) * width).toInt() - - /** - * 区域 - * */ - if (region.isNotEmpty()) { - region.clear() + MotionEvent.ACTION_DOWN -> { + dragVertex = findClosestVertex(x, y) + if (dragVertex != null) { + dragOffsetX = x - dragVertex!!.x + dragOffsetY = y - dragVertex!!.y + return true } - region.add(Point(leftTop.x / width, leftTop.y / height)) - region.add(Point(rightTop.x / width, rightTop.y / height)) - region.add(Point(leftBottom.x / width, leftBottom.y / height)) - region.add(Point(rightBottom.x / width, rightBottom.y / height)) + } - /** - * 点集合 - * */ - if (points.isNotEmpty()) { - points.clear() + MotionEvent.ACTION_MOVE -> { + if (dragVertex != null) { + dragVertex!!.set(x - dragOffsetX, y - dragOffsetY) + invalidate() // 重新绘制视图 + return true } - points.add(floatArrayOf(leftTop.x / width, leftTop.y / height)) - points.add(floatArrayOf(rightTop.x / width, rightTop.y / height)) - points.add(floatArrayOf(rightBottom.x / width, rightBottom.y / height)) - points.add(floatArrayOf(leftBottom.x / width, leftBottom.y / height)) + } + + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + dragVertex = null } } - invalidate() - return true + return super.onTouchEvent(event) } - fun clearRoutePath() { - routePath.reset() - rectPath.reset() - routes.clear() - invalidate() + // 查找最近的顶点 + private fun findClosestVertex(x: Float, y: Float): PointF? { + var closest: PointF? = null + var minDistance = Float.MAX_VALUE + for (vertex in vertices) { + val distance = sqrt( + (x - vertex.x).toDouble().pow(2.0) + (y - vertex.y).toDouble().pow(2.0) + ).toFloat() + if (distance < minDistance) { + minDistance = distance + closest = vertex + } + } + return if (minDistance < 50) { + closest + } else null } - /////////////////////////////////////////////////////////////////////////////////////////////// + fun getConfirmedPoints(): ArrayList { + /** + * 计算出点的相对位置返回给一体机计算 + * */ + val width = ctx.getScreenWidth() + val height = ((9 / 16f) * width).toInt() - private var region = ArrayList() - - fun getConfirmedRegion(): ArrayList = region - - /////////////////////////////////////////////////////////////////////////////////////////////// - - private var points = ArrayList() - - fun getConfirmedPoints(): ArrayList = points + val region = ArrayList() + region.add(floatArrayOf(vertices[0].x / width, vertices[0].y / height)) + region.add(floatArrayOf(vertices[1].x / width, vertices[1].y / height)) + region.add(floatArrayOf(vertices[2].x / width, vertices[2].y / height)) + region.add(floatArrayOf(vertices[3].x / width, vertices[3].y / height)) + return region + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_device_control_by_native.xml b/app/src/main/res/layout/activity_device_control_by_native.xml index 0e955f6..d0997ef 100644 --- a/app/src/main/res/layout/activity_device_control_by_native.xml +++ b/app/src/main/res/layout/activity_device_control_by_native.xml @@ -39,7 +39,7 @@ + android:layout_height="match_parent" />