注释解释
This commit is contained in:
@@ -45,7 +45,7 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
|
||||
private var selectedZone: Zone = Zone.CHEST
|
||||
|
||||
// Touch tracking for ImageView (captured in onTouch, used in ACTION_UP for click detection)
|
||||
// ImageView 的触控追踪(在 onTouch 中捕获,ACTION_UP 用于点击检测)
|
||||
private var lastTouchX = 0f
|
||||
private var lastTouchY = 0f
|
||||
|
||||
@@ -67,7 +67,7 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
binding.btnSubmit.setOnClickListener { showConfirmDialog() }
|
||||
|
||||
// Use OnTouchListener to track touch coordinates
|
||||
// 使用 OnTouchListen 来跟踪触摸坐标
|
||||
binding.ivHumanBody.setOnTouchListener { _, event ->
|
||||
lastTouchX = event.x
|
||||
lastTouchY = event.y
|
||||
@@ -77,7 +77,7 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
false
|
||||
}
|
||||
|
||||
// OnClick fires on ACTION_UP; coordinates captured from lastTouchX/Y
|
||||
// OnClick 对ACTION_UP开火;坐标来自lastTouchXY
|
||||
binding.ivHumanBody.setOnClickListener {
|
||||
val imageView = it as ImageView
|
||||
val (nx, ny) = resolveTapLocation(lastTouchX, lastTouchY, imageView)
|
||||
@@ -100,10 +100,12 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a screen coordinate (from onTouch) into a normalised [0..1] offset
|
||||
* inside the drawable, accounting for:
|
||||
* - ImageView scaling (scaleType="fitCenter" adds letterboxing)
|
||||
* - Letterbox / pillarbox margins when image aspect ≠ view aspect
|
||||
* 这段代码将触摸坐标转换为图片内部的归一化位置:
|
||||
*
|
||||
* 1. 计算图片等比缩放后的实际尺寸
|
||||
* 2. 计算letterbox/pillarbox的偏移量
|
||||
* 3. 将触摸点限制在图片有效区域内
|
||||
* 4. 返回相对于图片的归一化坐标(0-1范围)
|
||||
*/
|
||||
private fun resolveTapLocation(rawX: Float, rawY: Float, imageView: ImageView): Pair<Float, Float> {
|
||||
val drawable = imageView.drawable ?: return Pair(0f, 0f)
|
||||
@@ -114,22 +116,22 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
val intrinsicWidth = drawable.intrinsicWidth.toFloat()
|
||||
val intrinsicHeight = drawable.intrinsicHeight.toFloat()
|
||||
|
||||
// scale: the uniform factor the image is scaled by to fit the view
|
||||
// 比例:图像被缩放以适应视图的均匀因子
|
||||
val scale = min(ivWidth / intrinsicWidth, ivHeight / intrinsicHeight)
|
||||
|
||||
// Physical pixel size of the drawable after scaling
|
||||
// 缩放后绘制的物理像素大小
|
||||
val drawnW = intrinsicWidth * scale
|
||||
val drawnH = intrinsicHeight * scale
|
||||
|
||||
// Letterbox offsets (top / left) when aspect ratios differ
|
||||
// 当宽高比不同时,信箱偏移(左上角)
|
||||
val drawableLeft = (ivWidth - drawnW) / 2f
|
||||
val drawableTop = (ivHeight - drawnH) / 2f
|
||||
|
||||
// Clamp touch to the actual drawable area
|
||||
// 夹紧触地接触实际可绘制区域
|
||||
val clampedX = max(drawableLeft, min(rawX, drawableLeft + drawnW))
|
||||
val clampedY = max(drawableTop, min(rawY, drawableTop + drawnH))
|
||||
|
||||
// Normalised offset [0..1] within the drawable
|
||||
// 可绘制物内的归一化偏移 [0..1]
|
||||
val normalizedX = (clampedX - drawableLeft) / drawnW
|
||||
val normalizedY = (clampedY - drawableTop) / drawnH
|
||||
|
||||
@@ -137,14 +139,11 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Zones are divided by:
|
||||
* - Horizontal boundary at ny = 0.5 → top = HEAD, bottom = shoulders
|
||||
* - Vertical boundary 1 at nx = 1/3 → left = LEFT_SHOULDER
|
||||
* - Vertical boundary 2 at nx = 2/3 → right = RIGHT_SHOULDER, middle = CHEST
|
||||
* 这段代码根据归一化坐标确定触摸区域:
|
||||
*
|
||||
* If the touch falls within ±tolerance of any boundary line, the nearest
|
||||
* zone centre (Euclidean distance in normalised space) is used to avoid
|
||||
* arbitrary assignments at boundary pixels.
|
||||
* 1. 将图片分为头部、左肩、胸部、右肩四个区域
|
||||
* 2. 边界附近±0.04范围内,使用最近区域中心避免误判
|
||||
* 3. 上半部分为头部,下半部分按1/3和2/3垂直线划分
|
||||
*/
|
||||
private fun zoneForLocation(nx: Float, ny: Float): Zone {
|
||||
val hBoundary = 0.5f
|
||||
@@ -168,7 +167,14 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the Zone whose normalised centre is closest (Euclidean) to (nx, ny). */
|
||||
/**
|
||||
* 这段代码找到距离触摸点最近的区域:
|
||||
*
|
||||
* 1. 遍历所有Zone,计算与触摸点的欧氏距离平方
|
||||
* 2. 返回距离最小的Zone作为结果
|
||||
* 3. 若无匹配则默认返回CHEST
|
||||
* */
|
||||
|
||||
private fun nearestZone(nx: Float, ny: Float): Zone {
|
||||
return Zone.values().minByOrNull { zone ->
|
||||
val dx = nx - zone.normalizedX
|
||||
@@ -178,12 +184,12 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions the marker (ivStrikeMarker) at the zone's centre, clamped so the
|
||||
* marker never goes out of the ivHumanBody bounds.
|
||||
*
|
||||
* Because the marker is a child of ivHumanBody and starts at its center
|
||||
* (layout_gravity="center"), we compute the offset from that center point
|
||||
* and apply it via translationX/Y so the marker's layout bounds stay intact.
|
||||
这段代码更新标记位置到区域中心:
|
||||
|
||||
1. 根据Zone的归一化坐标计算目标位置
|
||||
2. 限制标记不超出图片边界(Y仅限制上限)
|
||||
3. 相对于ImageView中心计算偏移量并应用
|
||||
4. 支持动画或即时移动两种方式
|
||||
*/
|
||||
private fun updateMarkerPosition(zone: Zone, animated: Boolean) {
|
||||
if (humanBodyWidth <= 0 || humanBodyHeight <= 0) return
|
||||
@@ -198,9 +204,7 @@ class StrikePositionActivity : AppCompatActivity() {
|
||||
val rawX = humanBodyWidth * zone.normalizedX
|
||||
val rawY = humanBodyHeight * zone.normalizedY
|
||||
|
||||
// Clamp X so the marker never extends beyond the drawable edges.
|
||||
// Y clamp only has an upper bound — HEAD at normalizedY=0 is allowed to go
|
||||
// all the way to the top (translationY can be negative).
|
||||
//用X夹住,使标记永远不会超出可绘制的边缘。Y钳只有一个上界——在归一化Y=0处的HEAD可以一直达到顶端(平移Y可以为负)。
|
||||
val clampedX = min(max(rawX, halfMarkerW), humanBodyWidth - halfMarkerW)
|
||||
val clampedY = min(rawY, humanBodyHeight - halfMarkerH)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user