需求:單張人體圖片(1160x638),點擊不同部位(頭、手、胸、腹、腿)彈出對應Toast。核心思路:**預定義部位矩形區域(相對圖片坐標),觸摸事件坐標轉換後碰撞檢測**。本文提供完整自定義View實現方案。
class BodyPart {
String name; // "head", "left_arm"...
String label; // 顯示文本 "頭部", "左手臂"
RectF bounds; // 相對圖片原始尺寸的矩形區域 (left, top, right, bottom)
// 可擴展:點擊動作、關聯資源ID等
}
// 初始化配置(可放JSON/assets/body_parts.json動態加載)
List parts = Arrays.asList(
new BodyPart("head", "頭部", new RectF(400, 50, 760, 200)),
new BodyPart("left_arm", "左手臂", new RectF(100, 250, 300, 400)),
// ... 其他部位
);
public class BodyMapView extends View {
private Bitmap bodyBitmap;
private List parts;
private Matrix matrix = new Matrix(); // 圖片繪製變換矩陣
private RectF drawRect = new RectF(); // 實際繪製區域
public BodyMapView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
bodyBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.body_map);
// 初始化 parts 數據...
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 計算圖片縮放居中繪製矩陣
float scale = Math.min((float)w / bodyBitmap.getWidth(), (float)h / bodyBitmap.getHeight());
matrix.setScale(scale, scale);
matrix.postTranslate((w - bodyBitmap.getWidth() * scale) / 2,
(h - bodyBitmap.getHeight() * scale) / 2);
matrix.mapRect(drawRect, new RectF(0, 0, bodyBitmap.getWidth(), bodyBitmap.getHeight()));
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bodyBitmap, matrix, null);
// 可選:調試繪製熱區邊框
// for (BodyPart p : parts) { canvas.drawRect(transformRect(p.bounds), paint); }
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
float touchX = event.getX();
float touchY = event.getY();
// 1. 檢查是否在圖片繪製區域內
if (!drawRect.contains(touchX, touchY)) return true;
// 2. 觸摸點映射回圖片原始坐標系
float[] pts = {touchX, touchY};
Matrix inverse = new Matrix();
matrix.invert(inverse);
inverse.mapPoints(pts);
float imgX = pts[0], imgY = pts[1];
// 3. 遍歷部位檢測碰撞
for (BodyPart part : parts) {
if (part.bounds.contains(imgX, imgY)) {
Toast.makeText(getContext(), part.label, Toast.LENGTH_SHORT).show();
// 可回調接口通知 Activity/Fragment
if (listener != null) listener.onPartClick(part);
return true;
}
}
}
return super.onTouchEvent(event);
}
// 輔助:矩陣變換 RectF
private RectF transformRect(RectF src) {
RectF dst = new RectF();
matrix.mapRect(dst, src);
return dst;
}
public interface OnPartClickListener {
void onPartClick(BodyPart part);
}
private OnPartClickListener listener;
public void setOnPartClickListener(OnPartClickListener l) { listener = l; }
}
核心模式:原圖坐標定熱區,觸摸點逆矩陣映射回原圖,RectF/Path碰撞檢測,數據驅動UI。