我试图获得一个视图的左上角的绝对屏幕像素坐标。然而,我能找到的所有方法,如getLeft()和getRight()都不工作,因为它们似乎都是相对于视图的父视图,因此给我0。正确的做法是什么?

If it helps, this is for a 'put the picture back in order' game. I want the user to be able to draw a box to select multiple pieces. My assumption is that the easiest way to do that is to getRawX() and getRawY() from the MotionEvent and then compare those values against the top left corner of the layout holding the pieces. Knowing the size of the pieces, I can then determine how many pieces have been selected. I realise I can use getX() and getY() on the MotionEvent, but as that returns a relative position that makes determining which pieces were selected more difficult. (Not impossible, I know, but it seems unnecessarily complicated).

编辑:这是我用来尝试得到持有容器的大小的代码,每一个问题。tableelayout是包含所有拼图碎片的表格。

TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
Log.d(LOG_TAG, "Values " + tableLayout.getTop() + tableLayout.getLeft());

编辑2:下面是我尝试过的代码,下面是更多建议的答案。

public int[] tableLayoutCorners = new int[2];
(...)

TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
tableLayout.requestLayout();
Rect corners = new Rect();
tableLayout.getLocalVisibleRect(corners);
Log.d(LOG_TAG, "Top left " + corners.top + ", " + corners.left + ", " + corners.right
            + ", " + corners.bottom);

cells[4].getLocationOnScreen(tableLayoutCorners);
Log.d(LOG_TAG, "Values " + tableLayoutCorners[0] + ", " + tableLayoutCorners[1]);

这段代码是在所有初始化完成后添加的。图像被划分为一个包含在tableelayout中的ImageViews数组(cells[]数组)。单元格[0]是ImageView的左上角,我选择单元格[4]是因为它在中间的某个地方而且它的坐标肯定不应该是(0,0)

上面显示的代码仍然在日志中给我所有的0,我真的不明白,因为各种拼图块被正确显示。(我尝试了公共int table elayoutcorners和默认可见性,两者都给出相同的结果。)

我不知道这是否重要,但ImageViews最初没有给定大小。ImageViews的大小是在初始化过程中由视图自动决定的,当我给它一个图像来显示。这是否会导致它们的值为0,即使日志代码是在给它们一个图像并自动调整自己的大小之后?为了潜在地解决这个问题,我添加了如上所示的tableLayout.requestLayout()代码,但这并没有帮助。


当前回答

除了上面的答案,对于在哪里和何时调用getLocationOnScreen的问题?

对于与视图相关的任何信息,只有在视图被布局(创建)到屏幕上之后才可用。所以要获得位置,把你的代码放在view.post(Runnable)中,它在view被布局后被调用,像这样:

view.post(new Runnable() {
            @Override
            public void run() {

               // This code will run when view created and rendered on screen

               // So as the answer to this question, you can put the code here
               int[] location = new int[2];
               myView.getLocationOnScreen(location);
               int x = location[0];
               int y = location[1];
            }
        });  

其他回答

根据Romain Guy的评论,以下是我如何修复它。希望它能帮助其他有这个问题的人。

I was indeed trying to get the positions of the views before they had been laid out on the screen but it wasn't at all obvious that was happening. Those lines had been placed after the initilisation code ran, so I assumed everything was ready. However, this code was still in onCreate(); by experimenting with Thread.sleep() I discovered that the layout is not actually finalised until after onCreate() all the way to onResume() had finished executing. So indeed, the code was trying to run before the layout had finished being positioned on the screen. By adding the code to an OnClickListener (or some other Listener) the correct values were obtained because it could only be fired after the layout had finished.


下面这句话是建议社区编辑的:

请使用onWindowfocuschanged(boolean hasFocus)

使用View.getLocationOnScreen()和/或getLocationInWindow()。

公认的答案实际上并没有告诉如何获得位置,所以这里有更多的细节。你传入一个长度为2的int数组,这些值被替换为视图的(x, y)坐标(左上角)。

int[] location = new int[2];
myView.getLocationOnScreen(location);
int x = location[0];
int y = location[1];

笔记

用getLocationInWindow替换getLocationOnScreen应该在大多数情况下给出相同的结果(参见这个答案)。但是,如果你是在一个较小的窗口,如对话框或自定义键盘,那么使用你将需要选择哪一个更适合你的需求。 如果你在onCreate中调用这个方法,你会得到(0,0),因为视图还没有被布局。你可以使用一个ViewTreeObserver来监听布局何时完成,你可以得到测量的坐标。(请看这个答案。)

第一个方法:

在Kotlin中,我们可以为view创建一个简单的扩展:

fun View.getLocationOnScreen(): Point
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return Point(location[0],location[1])
}

然后得到坐标:

val location = yourView.getLocationOnScreen()
val absX = location.x
val absY = location.y

第二种方式:

第二种方法更简单:

fun View.absX(): Int
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return location[0]
}

fun View.absY(): Int
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return location[1]
}

通过view.absX()和view.absY()获得绝对X

binding.ivStory.setOnTouchListener(object : View.OnTouchListener{
        override fun onTouch(p0: View?, event: MotionEvent?): Boolean {
            val x = event?.x
            vak y = event?.y

            when (event!!.action) {
                MotionEvent.ACTION_UP ->

                if (x != null) {
                   //Do your stuff                    }

                MotionEvent.ACTION_DOWN  -> {

                }
            }
            return true
        }

    })

当你触摸屏幕时,这是Kotlin获得屏幕视图坐标的方法。 确保你同时处理动作向上和向下,否则可能会导致多次点击/触摸。