Create ImageView that is round, so click will work on round area only android

喜你入骨 提交于 2020-12-30 06:59:06

问题


Hi I am creating tabla App,

For example

enter image description here

It should not respond outside of round but ImageView is Rectangle so it is responding

I am sure you can understand this problem

ImageView is rectangle but it's image is round, but I want to detect click only on round image...


回答1:


Thank you for your all support, based on your support I have done by below way and it is working perfect

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



回答2:


It would seem that you will have to calculate whether or not the user is touching within the circular view. This will have to be achieved by overriding the touch event of the custom ImageView class that I assume you have already written.

Originally I had thought that drawing a circular area would have been enough, but that is not the case.

Pseudocode:

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

I might now end up adding this to my ImageView class to expand upon it and provide touch events only within the image when I need them as well.

If the image goes all the way to the edges then the radius is a little easier to determine. Otherwise, there will need to be a little extra done to figure out what the radius of the actual area is.




回答3:


You can attach the View.OnTouchListener to your ImageView. In that listener has only one method called OnTouchListener#onTouch (View v, MotionEvent event). The event argument has methods which allow to get the touch coordinates.
When you obtain the touch coordinates, relative to the ImageView size, you can check if the following inequality is true: (x - x0) ^ 2 + (y - y0) ^ 2 <= R ^ 2, where (x,y) - ImageView center coordinates, (x0, y0) - touch coordinates, R is the ImageView drawable radius (in your case it will be the half of ImageView width).
If it's true you can propagate the touch event further and return false, otherwise return true.




回答4:


Based on the answer from Siddhpura Amit, I just found that with this approach, the touch does not get the ACTION_CANCEL event, so the view does not get untouched when you move out of the "Inside Circle" area. I used the following workaround to solve that:

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:


A simple solution to exclude the transparent area of an image.

Note: Use PNG image

 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:


here a good solution reference from @Kedar Tendolkar

selection base on PNG Image... Detect onTouch Image on non Transparent color.
so if you have an PNG image with transparent background it will help you detect on touch activity base on the image it self if the color is equal to transparent it will detect as a background and the click would be bypass.


**create 2 function** - 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;
}

Then set your onTouch Listener to your imageview

    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:


From my answer to: https://stackoverflow.com/a/64246201/12235376

You can manually reduce the clickable area to the indicated circular area if you intercept and filter the touch events, by implementing a View.OnTouchListener or overriding onTouchEvent(). The latter requires you to subclass your button, which is easy enough but maybe not quite as desirable, so here is an example of using View.OnTouchListener to do the job:

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

Inside activity onCreate():

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

Note that:

  • isInTouchArea() can be arbitrarily implemented to set the clickable area to whatever shape you want, even potentially dependent on complex conditions like the background image of the button, etc.

  • setOnTouchListener() is not specific to the Button class. This solution can be used with any type of view whatsoever.

  • It is not a good solution to just blindly filter all touch messages (or just ACTION_DOWN) based on their X and Y position (e.g. as suggested in other answers), as you thereby break the MotionEvent consistency guarantee. This solution instead filters out complete gestures (sequences of MotionEvent's starting with ACTION_DOWN and ending with ACTION_UP/ACTION_CANCEL, with possibly many other actions inbetween like ACTION_MOVE) based on their starting coordinates. This means that the approach doesn't break in multi-touch situations.



来源:https://stackoverflow.com/questions/28090386/create-imageview-that-is-round-so-click-will-work-on-round-area-only-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!