Android实现带可点击区域的图像映射 点击切换图片无响应如何解决
问题根因
- 布局结构错误:ScrollView仅允许存在1个直接子View,当前直接放置2个ImageView会导致两张图片垂直顺序排列,而非你预期的重叠状态,点击主图获取的坐标和掩码图的像素位置完全不匹配,无法命中预设的颜色区域
- 可见性配置错误:布局中两张图片的visibility均设为visible,掩码图未按需求隐藏
- 坐标未适配缩放:ImageView使用fitCenter缩放模式,触摸获取的View相对坐标和掩码图Bitmap的实际像素坐标没有做比例转换,取到的像素点存在偏差
- 取色逻辑不稳定:Android高版本中DrawingCache已被废弃,视图未完成渲染时调用会返回空Bitmap,无法正确获取对应位置的颜色值
- Touch逻辑冗余:ACTION_DOWN中给nextImage赋值的逻辑会被后续ACTION_UP的赋值完全覆盖,无效逻辑可能干扰正常执行
修复方案
1. 修改布局文件
ScrollView内部新增FrameLayout作为唯一直接子View,将两张ImageView放入FrameLayout实现堆叠,同时将掩码图设为invisible(不可见但保留原有尺寸位置):
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="15dp" android:background="#ffffff" tools:context=".MainActivity"> <ScrollView android:layout_width="match_parent" android:layout_height="521dp" android:scrollbarAlwaysDrawVerticalTrack="true"> <!-- 新增FrameLayout作为唯一子容器,堆叠两张图片 --> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/image_areas" android:layout_width="fill_parent" android:scaleType="fitCenter" android:layout_height="fill_parent" android:adjustViewBounds="true" android:contentDescription="Specs" android:visibility="invisible" android:src="@drawable/seatmapmask" /> <ImageView android:id="@+id/image" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="fitCenter" android:adjustViewBounds="true" android:contentDescription="Specs" android:src="@drawable/seatmap" /> </FrameLayout> </ScrollView> <ImageView android:id="@+id/backButton" android:layout_width="match_parent" android:layout_height="84dp" app:srcCompat="@drawable/go_back_up" /> </LinearLayout>
2. 新增坐标转换方法
适配ImageView的缩放模式,将触摸坐标转换为Bitmap对应的像素坐标:
private int[] getBitmapPosition(ImageView imageView, float touchX, float touchY) { Drawable drawable = imageView.getDrawable(); if (drawable == null) return new int[]{0, 0}; // 获取图片实际宽高 int bitmapWidth = drawable.getIntrinsicWidth(); int bitmapHeight = drawable.getIntrinsicHeight(); // 获取ImageView的宽高 int viewWidth = imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); int viewHeight = imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); // 计算缩放比例 float scale = Math.max((float) bitmapWidth / viewWidth, (float) bitmapHeight / viewHeight); // 计算图片在ImageView中的偏移量 float offsetX = (viewWidth - bitmapWidth / scale) / 2f; float offsetY = (viewHeight - bitmapHeight / scale) / 2f; // 转换为Bitmap的像素坐标 int bitmapX = (int) ((touchX - offsetX) * scale); int bitmapY = (int) ((touchY - offsetY) * scale); // 边界处理 bitmapX = Math.max(0, Math.min(bitmapX, bitmapWidth - 1)); bitmapY = Math.max(0, Math.min(bitmapY, bitmapHeight - 1)); return new int[]{bitmapX, bitmapY}; }
3. 优化取色逻辑,替换废弃的DrawingCache
public int getHotspotColor (int hotspotId, float touchX, float touchY) { ImageView img = (ImageView) findViewById (hotspotId); if (img == null || img.getDrawable() == null) { Log.d ("ImageAreasActivity", "Hot spot image not found"); return 0; } // 转换坐标 int[] bitmapPos = getBitmapPosition(img, touchX, touchY); int bitmapX = bitmapPos[0]; int bitmapY = bitmapPos[1]; // 直接从Drawable拿Bitmap,无需DrawingCache Bitmap hotspots = ((BitmapDrawable)img.getDrawable()).getBitmap(); return hotspots.getPixel(bitmapX, bitmapY); }
4. 清理onTouch冗余逻辑
public boolean onTouch (View v, MotionEvent ev) { boolean handledHere = false; final int action = ev.getAction(); final float evX = ev.getX(); final float evY = ev.getY(); int nextImage = -1; // 直接强转v为ImageView,v就是设置了TouchListener的主图实例 ImageView imageView = (ImageView) v; Integer tagNum = (Integer) imageView.getTag (); int currentResource = (tagNum == null) ? R.drawable.seatmap : tagNum.intValue (); switch (action) { // 按需保留ACTION_DOWN逻辑,不需要按下态可直接删除该分支 case MotionEvent.ACTION_DOWN : handledHere = true; break; case MotionEvent.ACTION_UP : int touchColor = getHotspotColor (R.id.image_areas, evX, evY); ColorTool ct = new ColorTool (); int tolerance = 25; nextImage = R.drawable.seatmap; if (ct.closeMatch (Color.RED, touchColor, tolerance)) nextImage = R.drawable.pre3; else if (ct.closeMatch (Color.BLUE, touchColor, tolerance)) nextImage = R.drawable.preorder1; else if (ct.closeMatch (Color.YELLOW, touchColor, tolerance)) nextImage = R.drawable.preorder2; else if (ct.closeMatch (Color.WHITE, touchColor, tolerance)) nextImage = R.drawable.seatmap; if (currentResource == nextImage) { nextImage = R.drawable.seatmap; } handledHere = true; break; default: handledHere = false; } if (handledHere && nextImage > 0) { imageView.setImageResource (nextImage); imageView.setTag (nextImage); } return handledHere; }
内容的提问来源于stack exchange,提问作者Evin Melek
相关产品推荐
相关产品推荐

