获取与视图相关的触摸坐标(相当于ScreenToClient)

20
我在我的Android应用程序中显示一张图片给用户看。
我希望能够知道他们点击了图片的哪个位置。
通过实现OnTouchListener,我可以轻松地获取屏幕坐标。
 private OnTouchListener image_Listener = new OnTouchListener(){
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_UP) {
            float x = event.getX();
            float y = event.getY();
            return true;
        }
        return false;
    }
};

然而,这些是绝对坐标。我真正需要的是一个将其转换为相对于视图的坐标的函数。

我已经做了一些调查,但似乎找不到什么有用的东西,除了我自己尝试实现ScreenToClient方法..

有人知道解决这个问题的好办法吗,还是我必须自己动手实现?

看起来像是可以使用的方法 - 这就是我在问的原因。

感谢大家抽出时间帮忙。

编辑:我不太确定这些坐标是否已经相对于视图了。我记得读到过坐标是绝对的,但是经过一些尝试后 - 我按照预期的方式工作了。对于给大家带来的困扰,非常抱歉。

3个回答

29

仅为回答,触摸是相对于视图的。 是的,它们是触摸的原始屏幕坐标。 然而,由于您将触摸事件注册到视图本身,它只会在用户实际触摸视图时触发。 这意味着,即使屏幕坐标是原始屏幕坐标,它们也在视图本身的范围内。

如果您想要获取与该视图相关的触摸视图的x / y坐标,则可以像这样从屏幕坐标中减去左侧坐标,并从y坐标中减去上部坐标:

private OnTouchListener image_Listener = new OnTouchListener(){
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_UP) {
            float screenX = event.getX();
            float screenY = event.getY();
            float viewX = screenX - v.getLeft();
            float viewY = screenY - v.getTop();
            return true;
        }
        return false;
    }
};

如果用户在屏幕向下移动40个像素,但左边距设置为10,则 viewX 将为30。如果您将视图向下移动40个像素,并且用户在视图中触摸相同的位置,则仍然是30。

如果包括填充,则会稍微复杂一些,因为 View 内的填充仍然计算为 View,即使它不显示任何内容。

编辑:

我应该指出 Pierre 说的是正确的。如果您触摸一个视图,则它会继续接收触摸事件,直到手指抬起,即使您将手指拖动到视图范围之外。这意味着,ACTION_UP 可以接收不在视图范围内的触摸坐标。


我也遇到了同样的问题。从教程中可以看到,例如view.getLeft()这些方法“返回视图相对于其父视图的位置”。然而,在复杂的布局中,被调查的视图通常不是屏幕视图的直接子视图。因此,Deev提到的方法并不总是适用。有什么新的想法吗? - user1914692
6
嗯,我找到了一种方法:使用View.getLocationOnScreen()和/或getLocationInWindow()。 - user1914692

25

我刚刚遇到了同样的问题,尝试使用DeeV的解决方案时,getLeft()和getTop()方法总是返回0。由于更改布局不是一个选项(因为我认为getTop/Left仅相对于视图的父级),所以我必须想办法获取视图的绝对位置。

这里是一种类似的方法,使用getLocationOnScreen(location):

protected Point getRelativePosition(View v, MotionEvent event) {
    int[] location = new int[2];
    v.getLocationOnScreen(location);
    float screenX = event.getRawX();
    float screenY = event.getRawY();
    float viewX = screenX - location[0];
    float viewY = screenY - location[1];
    return new Point((int) viewX, (int) viewY);
}

这个可以工作,而 screenY - v.getTop() 不行(至少对我来说不行)。 - 18446744073709551615
v.getTop() 对我也不起作用。有人知道为什么吗? - eliasbagley
1
@eliasbagley:v.getTop() 是相对于 v 的父级而言的,而 screenY 是相对于屏幕的。只有当父级到达屏幕顶部时才能正常工作。 - user362178

3

触摸事件的坐标是相对于接收该事件的视图而言的。但它们不仅限于视图的大小,因为用户显然可以在同一动作中滑动到视图外。

直到触发 ACTION_UP 或 ACTION_CANCEL,即使指针在视图外部,视图仍将继续接收运动事件。

您确定监听器已在正确的视图中注册吗?


是的,我在正确的视图上进行了注册。一切都按照我的预期正常工作。我只是让事情变得比必要的更加困难。谢谢你的回复。 - syllogism
我认为“触摸事件坐标相对于视图...”这种说法是不正确的。例如,我有一个位于屏幕下方2/3处的按钮 - 将其附加并在其中心点击会得到一个y坐标,该坐标比按钮的高度大得多。因此,我认为这个说法是错误的。另一方面,我是通过Xamarin Forms实现的,所以完全有可能附件实际上并不是按钮[这将是Xamarin的一个错误]。 - ToolmakerSteve

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