【问题标题】:Create ImageView that is round, so click will work on round area only android创建圆形的 ImageView,因此单击仅适用于圆形区域 android
【发布时间】:2015-01-22 13:49:40
【问题描述】:

您好,我正在创建tabla App,

例如

它不应该在圆形之外响应,但 ImageView 是 Rectangle 所以它正在响应

相信你能理解这个问题

ImageView 是矩形,但它的图像是圆形的,但我只想检测圆形图像上的点击...

【问题讨论】:

标签: android android-layout android-view


【解决方案1】:

感谢您的所有支持,基于您的支持,我通过以下方式完成了工作,并且运行良好

ImageView imgView = (ImageView) findViewById(R.id.imageView1);
        imgView.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {

                //CIRCLE :      (x-a)^2 + (y-b)^2 = r^2 
                float centerX, centerY, touchX, touchY, radius;
                centerX = v.getWidth() / 2;
                centerY = v.getHeight() / 2;
                touchX = event.getX();
                touchY = event.getY();
                radius = centerX;
                System.out.println("centerX = "+centerX+", centerY = "+centerY);
                System.out.println("touchX = "+touchX+", touchY = "+touchY);
                System.out.println("radius = "+radius);
                if (Math.pow(touchX - centerX, 2)
                        + Math.pow(touchY - centerY, 2) < Math.pow(radius, 2)) {
                    System.out.println("Inside Circle");
                    return false;
                } else {
                    System.out.println("Outside Circle");
                    return true;
                }
            }
        });

【讨论】:

  • 你好@SiddhpuraAmit 我想第一次点击图片圈添加和第二次相同的地方点击圈删除它是可能的?
【解决方案2】:

您似乎必须计算用户是否在圆形视图内进行触摸。这必须通过覆盖我假设您已经编写的自定义 ImageView 类的触摸事件来实现。

原本我以为画一个圆形区域就足够了,但事实并非如此。

伪代码:

public class CustomImageView implements ImageView
{
    private Point centerPoint;
    private float radius;

    @Override
    protected void onDraw(Canvas canvasF) 
    {
        Drawable drawable = getDrawable();
        if(centerPoint == null)
        {
            centerPoint = new Point (getWidth() / 2, getHeight() / 2);
            /* 
             * if radius extends to edges, but if circular code 
             * exists already then we should already know what the 
             * radius is at this point I would assume.
             */
            radius = getWidth() / 2;
        }

        /*
         * remaining draw code for manipulating a circle.
         */
    }

    private boolean isInsideCircle(Point touchedPoint)
    {
         int distance = (int) Math.round(Math.pow(touchedPoint.x - centerPoint.x, 2) + Math.pow(touchedPoint.y - centerPoint.y, 2));

         if(distance < Math.pow(radius, 2))
         {
             return true;
         }
         else
         {
             return false;
         }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        Point touchedPoint = new Point(Math.round(event.getX()),   Math.round(event.getY()));

        if(isInsideCircle(touchedPoint))
        {
            return super.onTouchEvent(event);
        }

        return true;
    }
}

我现在可能最终将它添加到我的 ImageView 类中以对其进行扩展,并仅在我需要它们时在图像中提供触摸事件。

如果图像一直到边缘,则半径更容易确定。否则,需要做一些额外的工作来确定实际区域的半径是多少。

【讨论】:

  • 感谢您的帮助,为此 +1
【解决方案3】:

您可以将View.OnTouchListener 附加到您的ImageView。在那个监听器中只有一种方法叫做OnTouchListener#onTouch (View v, MotionEvent event)event 参数具有允许获取触摸坐标的方法。
获取到触摸坐标时,相对于ImageView的大小,可以检查以下不等式是否为true(x - x0) ^ 2 + (y - y0) ^ 2 &lt;= R ^ 2,其中(x,y)-ImageView中心坐标,(x0, y0)-触摸坐标,@ 987654331@ 是ImageView 可绘制半径(在您的情况下,它将是ImageView 宽度的一半)。
如果是true,则可以进一步传播触摸事件并返回false,否则返回true

【讨论】:

  • 再次感谢您的帮助,但应该是 X0-x 和 Y0-Y,TouchX-CenterX
  • @SiddhpuraAmit 不管是x0 - x 还是x - x0,因为它是平方的:(x - x0) ^ 2。之后它会是同一个正数。
  • 哦,我很抱歉,你是对的 :) 但是非常感谢
【解决方案4】:

根据 Siddhpura Amit 的回答,我刚刚发现使用这种方法,触摸不会获得 ACTION_CANCEL 事件,因此当您移出“Inside Circle”区域时,视图不会保持不变。 我使用以下解决方法来解决这个问题:

ImageView imgView = (ImageView) findViewById(R.id.imageView1);
imgView.setOnTouchListener(new View.OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
         //CIRCLE :      (x-a)^2 + (y-b)^2 = r^2 
        float centerX = v.getWidth() / 2;
        float centerY = v.getHeight() / 2;
        float touchX = event.getX();
        float touchY = event.getY();
        float radius = centerX;
        Log.d(TAG, "centerX = "+centerX+", centerY = "+centerY);
        Log.d(TAG, "touchX = "+touchX+", touchY = "+touchY);
        Log.d(TAG, "radius = "+radius);
        if (Math.pow(touchX - centerX, 2) + Math.pow(touchY - centerY, 2) < Math.pow(radius, 2)) {
            Log.d(TAG, "Inside Circle");
        } else {
            Log.d(TAG, "Outside Circle");
            if (event.getAction() != MotionEvent.ACTION_CANCEL) {
                event.setAction(MotionEvent.ACTION_CANCEL);
                v.dispatchTouchEvent(event);
                return true;
            }
        }
        return false;
    }
});

【讨论】:

    【解决方案5】:

    排除图像透明区域的简单解决方案。

    注意:使用PNG图片

     mImageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    if (view == null) return false;
                    Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache());
                    if (motionEvent.getX() < bmp.getWidth() && motionEvent.getY() < bmp.getHeight()) {
                        //Get color at point of touch
                        int color = bmp.getPixel((int) motionEvent.getX(), (int) motionEvent.getY());
                        bmp.recycle();
                        if (color == Color.TRANSPARENT) {
                            //do not proceed if color is transparent
                            return false;
    
                        } else {
                            //proceed if color is not transparent
                            return true;
                        }
                    }
                }
                return false;
            }
        });
    

    【讨论】:

      【解决方案6】:

      @Kedar Tendolkar

      提供了一个很好的解决方案参考

      基于 PNG 图像的选择...检测 onTouch 图像上的 非透明 颜色。
      因此,如果您有一个具有透明背景的 PNG 图像,它将帮助您根据自己的图像检测触摸活动,如果颜色等于透明,它将检测为背景并绕过点击。


      **创建 2 个函数** - 设置绘图缓存 - onTouchImageTransparent

      setDrawingCache

      public void setDrawingCache(View view){
              view.setDrawingCacheEnabled(true);
              view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                      View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
              view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
              view.buildDrawingCache(true);
          }
      

      onTouchImageTransparent

      public boolean onTouchImageTransparent(View view, MotionEvent motionEvent){
          if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
              Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache());
              if (motionEvent.getX() < bmp.getWidth() && motionEvent.getY() < bmp.getHeight()) {
                  //Get color at point of touch
                  int color = bmp.getPixel((int) motionEvent.getX(), (int) motionEvent.getY());
                  bmp.recycle();
                  if (color == Color.TRANSPARENT) {
                      //do not proceed if color is transparent
                      Log.d("onTouch","Click on Background PNG");
                      return false;
      
                  } else {
                      //proceed if color is not transparent
                      Log.d("onTouch","Click on Image");
                      return true;
                  }
              }else{
                  Log.d("onTouch","Click on somewhere else");
                  return false;
              }
          }else{
              Log.d("onTouch","Click on Background");
          }
          return false;
      }
      

      然后将您的 onTouch 侦听器设置为您的图像视图

          ImageView mImageView = findViewById(R.id.imageView);
          setDrawingCache(mImageView);
          mImageView.setOnTouchListener(new View.OnTouchListener() {
              @Override
              public boolean onTouch(View view, MotionEvent motionEvent) {
                  if(onTouchImageTransparent(view,motionEvent)){
                      //action goes here
                  }
                  return onTouchImageTransparent(view,motionEvent);
              }
          });
      

      【讨论】:

        【解决方案7】:

        我的回答是:https://stackoverflow.com/a/64246201/12235376

        如果您拦截和过滤触摸事件,您可以手动将可点击区域缩小到指示的圆形区域,方法是实现View.OnTouchListener 或覆盖onTouchEvent()。后者要求您对按钮进行子类化,这很简单,但可能不太理想,所以这里有一个使用 View.OnTouchListener 来完成这项工作的示例:

        OvalTouchAreaFilter.java:

        public class OvalTouchAreaFilter implements View.OnTouchListener {
        
            private boolean mIgnoreCurrentGesture;
        
            public TouchAreaFilter() {
                mIgnoreCurrentGesture = false;
            }
        
            public boolean isInTouchArea(View view, float x, float y) {
                int w = view.getWidth();
                int h = view.getHeight();
                if(w <= 0 || h <= 0)
                    return false;
                float xhat = 2*x / w - 1;
                float yhat = 2*y / h - 1;
                return (xhat * xhat + yhat * yhat <= 1);
            }
        
            @SuppressLint("ClickableViewAccessibility")
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                int action = event.getActionMasked();
                if(action == MotionEvent.ACTION_DOWN) {
                    mIgnoreCurrentGesture = !this.isInTouchArea(view, event.getX(), event.getY());
                    return mIgnoreCurrentGesture;
                }
                boolean ignoreCurrentGesture = mIgnoreCurrentGesture;
                if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)
                    mIgnoreCurrentGesture = false;
                return ignoreCurrentGesture;
            }
        }
        

        内部活动 onCreate():

        View button = findViewById(R.id.my_button);
        button.setOnTouchListener(new OvalTouchAreaFilter());
        

        注意:

        • isInTouchArea() 可以任意实现,将可点击区域设置为您想要的任何形状,甚至可能依赖于复杂条件,例如按钮的背景图像等。

        • setOnTouchListener() 不特定于 Button 类。此解决方案可用于任何类型的视图。

        • 仅根据 X 和 Y 位置(例如,如其他答案中所建议的)盲目过滤所有触摸消息(或仅 ACTION_DOWN)并不是一个好的解决方案,因为您因此会破坏 MotionEvent consistency guarantee。这个解决方案会过滤掉完整的手势MotionEvent 的序列以ACTION_DOWN 开头并以ACTION_UP/ACTION_CANCEL 结尾,中间可能还有许多其他动作,例如ACTION_MOVE)在他们的起始坐标上。这意味着该方法在多点触控情况下不会中断。

        【讨论】:

          猜你喜欢
          • 2019-10-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-24
          • 1970-01-01
          • 2013-04-18
          • 1970-01-01
          相关资源
          最近更新 更多