为同一图像的不同部分设置单击侦听器

时间:2018-05-30 18:30:06

标签: android android-layout user-interface

我有一个看起来像这样的单身图像

enter image description here

我需要做的是区分对身体不同部位的点击。 例如,如果单击头部,则应执行函数onHeadClicked。如果我单击左侧,则单击一个函数onLeftHandClicked。这将如何运作?

2 个答案:

答案 0 :(得分:1)

首先,您必须将实体点的坐标存储在实际图像中。然后只需检查点击坐标是否在身体点的坐标内。您还应确保使用imageview的矩阵及其在屏幕上的坐标来缩放点

例如,如果

        class BodyPoint{
              String name;
              int x;
              int y;
              public(String name,int x,int y){
                   this.name = name;
                   this.x = x;
                   this.y = y;}
             }
        BodyPoint headCoordinates = new BodyPoint ("head",50,20);
        BodyPoint neckCoordinates = new BodyPoint ("neck",50,50);
        BodyPoint leftHandCoordinates = new BodyPoint ("leftHand",10,50);
        BodyPoint rightHandCoordinates = new BodyPoint ("rightHand",80,50);

        BodyPoint[] bodyCoordinates = new BodyPoint[]{headCoordinates,neckCoordinates,
        leftHandCoordinates ,rightHandCoordinates };

    yourImgView.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
             int[] viewCoords = new int[2];
                yourImgView.getLocationOnScreen(viewCoords);

    int touchX = (int) event.getX();
    int touchY = (int) event.getY();

    int imageX = touchX - viewCoords[0]; // viewCoords[0] is the X coordinate
    int imageY = touchY - viewCoords[1]; 
    Matrix mMatrix = getImageMatrix();
    float[] f = new float[9];
    mMatrix .getValues(f);

   float scaleX = f[Matrix.MSCALE_X];
   float scaleY = f[Matrix.MSCALE_Y];

    processTouchedPoints(imageX/(int)scaleX , imageY/(int)scaleY );
            return true;

        }
    });

    ...
    int range = 50;
    void processTouchedPoints(int imageX,int imageY){
      foreach(BodyPoint mBodyPoint:bodyCoordinates ){
    int x = mBodyPoint.x;
    int y = mBodyPoint.y;
          if((imageX> (x-range) && imageX<(x+range))
           &&(imageY> (y-range) && imageY<(y+range))){
           doWhatever(mBodyPoint.name)
         }
      }
    }

答案 1 :(得分:1)

警告:此解决方案至少有5年(我还在使用它),所以肯定已经过时了,也许有新技术可以解决这个问题。无论如何,我会在这里发布解决方案以帮助您。此外,这个解决方案有点麻烦,你可以重构或改进它,随意做。

摘要

此解决方案基于2个图像,其中一个图像将可见(您要向用户显示哪个图像),另一个图像将不可见。

不可见图像必须具有与目标图像相同的像素大小(在您的情况下是身体图像),将是透明的(或黑色背景),并且您将要点击的不同区域填充不同的颜色。< / p>

因此,当您单击目标图像时,将检查其矩阵的坐标,然后从该坐标开始,从第二个图像中获取按下的像素的颜色。

由于您知道哪种颜色与身体的哪个部分相对应(因为您之前已经使用color-&gt; part_of_body配置了地图),因此获取一种颜色就可以确切地知道身体的一部分被点击。

解决方案

您有目标图片:

enter image description here

然后,您需要创建第二个图像,如下所示:

enter image description here

看到你现在身体的每个部位都标有明显的颜色。请注意,颜色明显不同。这就是为什么如果你使用相似的颜色,当时可能会有冲突来检索身体的一部分,因为颜色可能会混淆。

之后,您需要使用透明背景导出颜色图像,您应该得到以下内容(请注意背景是透明的,但StackOverflow的背景是白色的):

enter image description here

您将使用第一张和第三张图片,第二张图片只是一个中间步骤。

首先,您必须在代码中配置您的地图颜色 - &gt; part_of_body:

public HashMap<Integer, String> bodyParts;
bodyParts.put(parseColor("#ff0000"), "part_1");
bodyParts.put(parseColor("#00ff00"), "part_2");
bodyParts.put(parseColor("#0000ff"), "part_3");
bodyParts.put(parseColor("#ffff00"), "part_4");
... // Finish here with all your parts and colors

然后我制作了自定义ImageView,以便更轻松地处理图片:

<强> ZoneTapImageView.java

    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Color;
    import android.graphics.Matrix;
    import android.graphics.RectF;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.FrameLayout;
    import android.widget.ImageView;

    import java.util.HashMap;

    import uk.co.senab.photoview.PhotoViewAttacher;

    public class ZoneTapImageView extends FrameLayout implements PhotoViewAttacher.OnPhotoTapListener {
      private static final String LOG_TAG = "ZoneMapTouch";
      private static final int DEFAULT_TOLERANCE = 25;

      private ImageView imageView;
      private ImageView imageViewAreas;
      @SuppressLint("UseSparseArrays")
      private HashMap<Integer, String> areasMap = new HashMap<>();
      private Context context;
      private OnAreaObtainedListener areaObtainedListener;
      private PhotoViewAttacher imageViewAttacher;
      private PhotoViewAttacher imageViewAreasAttacher;

      public ZoneTapImageView(Context context) {
        super(context);
        init(context);
      }

      public ZoneTapImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
      }

      public ZoneTapImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
      }

      private void init(Context context) {
        int padding = getResources().getDimensionPixelSize(R.dimen.groups_padding_mini);

        this.context = context;
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        this.setLayoutParams(params);

        params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        params.gravity = Gravity.CENTER;

        imageView = new ImageView(getContext());
        imageView.setPadding(padding, padding, padding, padding);
        imageView.setLayoutParams(params);
        imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
        imageView.setAdjustViewBounds(true);

        imageViewAreas = new ImageView(getContext());
        imageViewAreas.setPadding(padding, padding, padding, padding);
        imageViewAreas.setLayoutParams(params);
        imageViewAreas.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
        imageViewAreas.setAdjustViewBounds(true);
        imageViewAreas.setVisibility(View.INVISIBLE);

        this.addView(imageViewAreas);
        this.addView(imageView);
      }

      public void setImageResources(int resIdImage, int resIdImageAreas) {
        setImageResource(resIdImage);
        setImageResourceAreas(resIdImageAreas);
      }

      public void setImageResource(final int resIdImage) {
        imageView.post(new Runnable() {
          @Override
          public void run() {
            // Here I use a Image Cache, but it's not necessary
            ImageMemoryCache.loadBitmap(context, resIdImage, imageView, imageView.getWidth(), imageView.getHeight(), new ImageMemoryCache.OnImageLoadedListener() {
              @Override
              public void onImageLoaded() {
                if (imageViewAttacher == null) {
                  imageViewAttacher = new PhotoViewAttacher(imageView);
                  imageViewAttacher.setZoomable(true);
                  imageViewAttacher.setOnPhotoTapListener(ZoneTapImageView.this);
                }
              }
            });
          }
        });
      }

      public void setImageResourceAreas(final int resIdImageAreas) {
        imageViewAreas.post(new Runnable() {
          @Override
          public void run() {
            // Here I use a Image Cache, but it's not necessary
            ImageMemoryCache.loadBitmap(context, resIdImageAreas, imageViewAreas, imageViewAreas.getWidth(), imageViewAreas.getHeight(), new ImageMemoryCache.OnImageLoadedListener() {
              @Override
              public void onImageLoaded() {
                if (imageViewAreasAttacher == null) {
                  imageViewAreasAttacher = new PhotoViewAttacher(imageViewAreas);
                  imageViewAreasAttacher.setZoomable(false);
                }
              }
            });
          }
        });
      }

      public void setZoomOut() {
        if (imageViewAttacher != null)
          imageViewAttacher.setScale(1, true);
      }

      public void setOnAreaObtainedListener(OnAreaObtainedListener areaObtainedListener) {
        this.areaObtainedListener = areaObtainedListener;
      }

      @Override
      public void onPhotoTap(View view, float x, float y) {
        if (imageViewAreasAttacher == null) return;
        final RectF displayRect = imageViewAreasAttacher.getDisplayRect();

        float xAbsolute = x * displayRect.width() + displayRect.left;
        float yAbsolute = y * displayRect.height() + displayRect.top;

        Log.d("MapTouch", "X: " + xAbsolute + " Y: " + yAbsolute);
        getAreaFromCoordinatesAsync((int) xAbsolute, (int) yAbsolute, areaObtainedListener);
      }

      public void setAreasMap(HashMap<Integer, String> areasMap) {
        this.areasMap = areasMap;
      }

      public void getAreaFromCoordinatesAsync(final int x, final int y, final OnAreaObtainedListener onAreaObtainedListener) {
        new Thread(new Runnable() {
          @Override
          public void run() {
            String area = getAreaFromCoordinates(x, y);
            if (onAreaObtainedListener != null) {
              onAreaObtainedListener.OnArea(area);
            }
          }
        }).start();
      }

      public String getAreaFromCoordinates(int x, int y) {
        int touchColor = getTapColor(x, y);
        Log.d(LOG_TAG, "Color (" + x + ", " + y + "): " + touchColor);
        if (touchColor == Integer.MIN_VALUE) return null;
        return getAreaFromColor(touchColor);
      }

      public String getAreaFromColor(int color) {
        for (Integer colorKey : areasMap.keySet()) {
          if (matchColor(colorKey, color, DEFAULT_TOLERANCE)) {
            return areasMap.get(colorKey);
          }
        }
        return null;
      }

      private boolean matchColor(int color1, int color2, int tolerance) {
        if (Math.abs(Color.red(color1) - Color.red(color2)) > tolerance)
          return false;
        if (Math.abs(Color.green(color1) - Color.green(color2)) > tolerance)
          return false;
        if (Math.abs(Color.blue(color1) - Color.blue(color2)) > tolerance)
          return false;
        return true;
      }

      private int getTapColor(int x, int y) {
        try {
          // Convert coordinates for scaled bitmap
          float[] eventXY = new float[]{x, y};
          Matrix invertMatrix = new Matrix();
          imageViewAreas.getImageMatrix().invert(invertMatrix);
          invertMatrix.mapPoints(eventXY);
          x = (int) eventXY[0];
          y = (int) eventXY[1];

          // Get bitmap
          Drawable imgDrawable = imageViewAreas.getDrawable();
          Bitmap bitmap = ((BitmapDrawable) imgDrawable).getBitmap();

          // Get color
          return bitmap.getPixel(x, y);
        } catch (Exception e) {
          return Integer.MIN_VALUE;
        }
      }
    }

您需要以下依赖才能使其正常运行:

compile 'com.github.chrisbanes.photoview:library:1.2.2'

要实例化上一个类,您可以按如下方式执行:

imageView = new ZoneTapImageView(getActivity());
imageView.setImageResources(R.drawable.body_image, R.drawable.invisielb_areas_image);
imageView.setAreasMap(bodyParts);
imageView.setOnAreaObtainedListener(new OnAreaObtainedListener() {
  @Override
  public void OnArea(final String area) {
    Log.d("MapTouch", "Area:  " + area);
    // Area here is such "part_1" or "part_2"
    if (area != null) {
      // Handle your bodyPart click
    }
  }
});

这就是全部。希望我没有错过任何代码,并希望这对你有所帮助。