Установить прослушиватели кликов для разных частей одного изображения - PullRequest
0 голосов
/ 30 мая 2018

У меня есть одно изображение тела, которое выглядит примерно так:

enter image description here

Что мне нужно сделать, это дифференцировать щелчок по различным частямтело.Например, если щелкнуть головой, должна выполняться функция onHeadClicked.Если я нажму на левую руку, то функция onLeftHandClicked.Как это будет работать?

Ответы [ 2 ]

0 голосов
/ 31 мая 2018

Предупреждение: Это решение имеет как минимум 5 лет (я все еще использую его), поэтому, безусловно, устарело и, возможно, есть новые технологии, чтобы лучше решить эту проблему.В любом случае, я опубликую решение здесь, чтобы помочь вам.Кроме того, это решение немного обременительно, и вы, возможно, могли бы изменить или улучшить его, не стесняйтесь делать это.

Резюме

Это решение основано на 2 изображениях, одно из которых будет видно(который вы хотите показать пользователю), а другой будет невидимым.

Невидимое изображение должно иметь такой же размер в пикселях, что и целевое изображение (в вашем случае изображение тела), оно будет прозрачным (или черным фоном) и будет иметь разные зоны, которые вы хотите щелкнуть, заполненными различнымиcolors.

Итак, когда вы щелкаете по целевому изображению, проверяются координаты его матрицы, а затем, начиная с этих координат, цвет нажатого пикселя получается из второго изображения.

Поскольку вы знаете, какой цвет соответствует какой части тела (поскольку ранее вы настроили карту с помощью color-> part_of_body), получив один цвет, вы сможете точно узнать, какая часть тела была нажата.

Решение

У вас есть целевое изображение:

enter image description here

Затем вам нужно создать второе изображение следующим образом:

enter image description here

Обратите внимание, что теперь у вас есть каждая часть тела, помеченная отдельным цветом.Обратите внимание, что цвета заметно отличаются.Вот почему, если вы используете похожие цвета, могут быть конфликты в то время, чтобы получить часть тела, потому что цвета могут быть перепутаны.

После этого вам нужно экспортировать изображение цветов с прозрачным фоном, и вы должны получитьследующее (обратите внимание, что фон прозрачный, но фон StackOverflow белый):

enter image description here

Вы будете работать с первым и третьим изображениями,второй - только промежуточный шаг.

Прежде всего, вам нужно настроить в коде цвет вашей карты-> 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
    }
  }
});

И это все.Надеюсь, я не пропустил ни одного кода и надеюсь, что это поможет вам.

0 голосов
/ 30 мая 2018

Во-первых, вы должны сохранить координаты точек тела на реальном изображении.А затем просто проверьте, находятся ли координаты щелчка в пределах координат точек тела.Вы также должны убедиться, что масштабируете точки, используя матрицу просмотра изображения и ее координаты на экране

Например, если

        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)
         }
      }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...