创建圆形的ImageView,因此单击将仅在圆形区域上工作

时间:2015-01-22 13:49:41

标签: android android-layout android-view

您好我正在创建tabla App,

例如

enter image description here

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

我相信你能理解这个问题

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

7 个答案:

答案 0 :(得分:6)

感谢您的支持,基于您通过以下方式提供的支持,并且工作正常

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;
                }
            }
        });

答案 1 :(得分:5)

您似乎必须计算用户是否在圆形视图中触摸。这必须通过覆盖我假设您已编写的自定义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类中以对其进行扩展,并在我需要时仅在图像中提供触摸事件。

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

答案 2 :(得分:2)

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

答案 3 :(得分:0)

根据Siddhpura Amit的回答,我发现通过这种方法,触摸不会获得ACTION_CANCEL事件,因此当你离开&#34; Inside Circle&#34;时,视图不会被触及。区域。 我使用以下解决方法来解决这个问题:

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;
    }
});

答案 4 :(得分:0)

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

注意:使用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;
        }
    });

答案 5 :(得分:0)

这里是 @Kedar Tendolkar

的很好的解决方案参考

基于PNG图片的选择...以非透明颜色检测 onTouch 图片。
因此,如果您的PNG图片具有透明的背景,则可以帮助您根据自身的图像检测触摸活动,如果颜色等于透明,则会检测为背景并且点击将被绕过。


**创建2个功能** -setDrawingCache -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 Listener设置为图像视图

    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);
        }
    });

答案 6 :(得分:0)

从我的回答到: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)不是一个好的解决方案,因为{3}}。相反,此解决方案可以过滤出完整手势MotionEvent的顺序以ACTION_DOWN开始,以ACTION_UP / ACTION_CANCEL结尾,可能还有许多其他手势ACTION_MOVE之间的动作基于其起始坐标。这意味着该方法在多点触摸情况下不会中断。