问题
Hi I am creating tabla App,
For example
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 theButton
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 ofMotionEvent
's starting withACTION_DOWN
and ending withACTION_UP
/ACTION_CANCEL
, with possibly many other actions inbetween likeACTION_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