如何判断一个X和Y坐标是否在我的按钮范围内?

15

我很费力地成功让一个位图覆盖在屏幕上,同时我也可以获取触摸输入,但是它会对屏幕上的每个地方都进行触摸输入。

我想知道如何检查触摸是否发生在我的位图上,因为它显示在屏幕上。

下面是服务和视图类。我已经思考了很久,但是我想不出怎么做 :(

package <package>;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Toast;

public class MyService extends Service {
    ButtonView mView;
    Bitmap bit;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        bit = BitmapFactory.decodeResource(getResources(), R.drawable.button);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(
                this);
        builder.setContentTitle("Ingress Tools Running");
        builder.setContentText("Click to stop Ingress Tools");
        builder.setSmallIcon(R.drawable.ic_launcher);
        builder.setContentIntent(PendingIntent.getActivity(this, 0, new Intent(
                this, StopActivity.class), 0));
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        manager.notify(1, builder.build());

        mView = new ButtonView(this, bit);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.FILL_PARENT,
                WindowManager.LayoutParams.FILL_PARENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.RIGHT;
        params.setTitle("Load Average");
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.addView(mView, params);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Toast.makeText(getBaseContext(), "onDestroy", Toast.LENGTH_LONG).show();
        if (mView != null) {
            ((WindowManager) getSystemService(WINDOW_SERVICE))
                    .removeView(mView);
            mView = null;
        }
    }
}

class ButtonView extends ViewGroup {
    private Paint mLoadPaint;
    private Rect r;
    private Bitmap bit;

    public ButtonView(Context context, Bitmap bit) {
        super(context);
        Toast.makeText(context, "HUDView", Toast.LENGTH_LONG).show();

        mLoadPaint = new Paint();
        mLoadPaint.setAntiAlias(true);
        mLoadPaint.setTextSize(10);
        mLoadPaint.setARGB(255, 255, 0, 0);
        r = new Rect();
        r.set(380, 134, 468, 213);
        this.bit = bit;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //canvas.drawColor(Color.BLACK);

        canvas.drawBitmap(bit, 100, 100, null);
    }

    @Override
    protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int area = bit.getWidth() * bit.getHeight();


        //if (event.getY() <= maxY && event.getX() <= maxX) {
            Toast.makeText(getContext(), "Open tools: ", Toast.LENGTH_LONG)
                    .show();

        //}
        return true;

    }
}
6个回答

22

这适用于任何视图

@Override
    public boolean dispatchTouchEvent(MotionEvent event) {

            if (event.getAction() == MotionEvent.ACTION_UP) {
                if (inViewInBounds(myButtonView, (int) event.getRawX(), (int) event.getRawY())) {
                    // User moved outside bounds
                    Log.e("dispatchTouchEvent", "you touched inside button");
                } else {
                    Log.e("dispatchTouchEvent", "you touched outside button");
                }

        }
        return super.dispatchTouchEvent(event);
    }

 Rect outRect = new Rect();
    int[] location = new int[2];


 private boolean inViewInBounds(View view, int x, int y) {
        view.getDrawingRect(outRect);
        view.getLocationOnScreen(location);
        outRect.offset(location[0], location[1]);
        return outRect.contains(x, y);
    }

1
代码可以执行,但我不理解这些函数。为什么在调用View::getDrawingRect之后,还要调用View::getLocationOnScreen呢?难道Rect不应该也有x和y位置吗?另外,我想Rect::offset()函数是用来重新定位矩形的,但我真的不太理解它。 - user9599745

20

考虑使用FrameLayout(或任何其他ViewGroup的子类)替代直接使用ViewGroup。因为您当前实现的onLayout方法不正确,这将导致子视图显示问题。

现在,回到你的问题。你应该初始化Rect并只存储Bitmap的左、上、右和下位置。我看到,目前你已经初始化了r变量,但没有在任何地方使用它。

所以,你可以像这样初始化:

r = new Rect(100, 100, 100 + bit.getWidth(), 100 + bit.getHeight());

现在在onTouchEvent中,您只需检查:

r.contains((int) event.getX(), (int) event.getY());

这个可以工作,但是现在其他的东西都无法接收到触摸事件了。也就是说,我无法点击屏幕上的其他任何东西(尽管在添加这个之前可能已经发生过)。 - Liam W
@LiamW 如果你想让你的 MotionEvent 继续传递下去,那么你应该从 onTouchEvent 返回 false。 - Dmitry Zaytsev
我在 if 语句之外返回 false,但如果我触摸图像之外,则不会发生任何事情。 - Liam W
@LiamW 这可能是因为您的视图(和窗口)是 FILL_PARENT - Dmitry Zaytsev

16
Rect rect = new Rect(); 
getHitRect(rect);
if (rect.contains((int) event.getX(), (int) event.getY())) {}

你可以使用getHitRect(Rect)方法,它会返回父控件坐标系中的点击矩形区域。 文档 在这里。


1
通常情况下,这是正确的方法。尽管如此,他绘制的是“位图”而不是“视图”,因此无法使用“getHitRect”。 - Dmitry Zaytsev

1

使用if语句和if(r.contains(x, y))方法来检查你想要检查的按钮。当x和y点在矩形r内时,该方法将返回true。你也可以在该类中创建公共方法,这样你就可以通过按钮对象引用在ButtonView类外部访问它。


0
// We assume MotionEvent is from the direct View parent so we are in the same co-ordindate space
fun View.isWithinBounds(event: MotionEvent): Boolean {
    val rect = Rect(x.roundToInt(), y.roundToInt(), (x + width).roundToInt(), (y + height).roundToInt())
    return rect.contains(event.x.roundToInt(), event.y.roundToInt())
}

-1

当发生“触摸事件”时,它会遍历所有视图树。 例如,如果您在线性布局上有ImageView并且用户触摸ImageView屏幕,则触摸事件将拦截并首先在LinearLayout上处理,然后在ImageView上处理。

如果您想在位图上阻止事件,那么您应该覆盖位图的onTouchEvent并返回true值。这意味着您处理了此事件,并且不会对LinearLayout可用。

image.setOnTouchListener( new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                return true;  // attentively read documentation for onTouc interface
            }
        });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接