如何获取视图的绝对坐标

发布于 2024-08-20 07:32:53 字数 1756 浏览 5 评论 0原文

我正在尝试获取视图左上角的绝对屏幕像素坐标。但是,我能找到的所有方法(例如 getLeft()getRight())都不起作用,因为它们似乎都与视图的父级相关,因此给出我0。执行此操作的正确方法是什么?

如果有帮助的话,这是一个“将图片恢复正常”的游戏。我希望用户能够绘制一个框来选择多个部分。我的假设是,最简单的方法是从 MotionEvent 中获取 getRawX()getRawY(),然后将这些值与放置棋子的布局的左上角。知道了碎片的大小,我就可以确定选择了多少碎片。我意识到我可以在 MotionEvent 上使用 getX()getY() ,但是这样会返回一个相对位置,从而确定哪些片段是选起来比较困难。 (我知道这并非不可能,但似乎不必要地复杂)。

编辑:这是我用来根据问题之一尝试获取容纳容器的大小的代码。 TableLayout 是容纳所有拼图的表格。

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

该代码是在所有初始化完成后添加的。图像已被划分为包含在 TableLayout 中的 ImageView 数组(cells[] 数组)。 Cells[0] 是左上角的 ImageView,我选择了 cells[4],因为它位于中间的某个位置,并且绝对不应该具有 (0,0) 坐标。

上面显示的代码在日志中仍然给我全0,我真的不明白,因为各个拼图都正确显示。 (我尝试了 tableLayoutCorners 的 public int 和默认可见性,两者都给出了相同的结果。)

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

I'm trying to get the absolute screen pixel coordinates of the top left corner of a view. However, all methods I can find such as getLeft() and getRight() don't work as they all seem to be relative to the parent of the view, thus giving me 0. What is the proper way to do this?

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).

Edit: This is the code I used to try to get the size of the holding container, as per one of the questions. TableLayout is the table which holds all the puzzle pieces.

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

Edit 2: Here is the code I've tried, following more of the suggested answers.

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

This code was added after all the initialisation is done. The image has been divided up into a array of ImageViews (the cells[] array) contained within a TableLayout. Cells[0] is the top left ImageView, and I picked cells[4] as it's somewhere in the middle and most definitely should not have coordinates of (0,0).

The code shown above still gives me all 0s in the logs, which I really don't understand because the various puzzle pieces are correctly displayed. (I tried public int for tableLayoutCorners and default visibility, both giving the same result.)

I don't know if this is significant, but the ImageViews are originally not given a size. The size of the ImageViews is determined during the initialisation automatically by the View when I give it an image to display. Could this contribute to their values being 0, even though that logging code is after they have been given an image and have automatically resized themselves? To potentially counter that, I added the code tableLayout.requestLayout() as shown above, but that didn't help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

美人骨 2024-08-27 07:32:53

接受的答案实际上并没有告诉如何获取位置,所以这里有更多细节。您传入长度为 2 的 int 数组,并将值替换为视图的(左上角)的 (x, y) 坐标。

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

注释

  • 替换 getLocationOnScreengetLocationInWindow 在大多数情况下应该给出相同的结果(请参阅此答案)。但是,如果您使用的是较小的窗口(例如对话框或自定义键盘),那么您将需要选择哪一个更适合您的需求。
  • 如果在 onCreate 中调用此方法,您将得到 (0,0),因为视图尚未布局。您可以使用 ViewTreeObserver 来侦听布局何时完成,并且可以获得测量的坐标。 (请参阅此答案。)

The accepted answer didn't actually tell how to get the location, so here is a little more detail. You pass in an int array of length 2 and the values are replaced with the view's (x, y) coordinates (of the top, left corner).

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

Notes

  • Replacing getLocationOnScreen with getLocationInWindow should give the same results in most cases (see this answer). However, if you are in a smaller window like a Dialog or custom keyboard, then use you will need to choose which one better suits your needs.
  • You will get (0,0) if you call this method in onCreate because the view has not been laid out yet. You can use a ViewTreeObserver to listen for when the layout is done and you can get the measured coordinates. (See this answer.)
挽心 2024-08-27 07:32:53

首先,您必须获取视图的 localVisible 矩形

例如:

Rect rectf = new Rect();

//For coordinates location relative to the parent
anyView.getLocalVisibleRect(rectf);

//For coordinates location relative to the screen/display
anyView.getGlobalVisibleRect(rectf);

Log.d("WIDTH        :", String.valueOf(rectf.width()));
Log.d("HEIGHT       :", String.valueOf(rectf.height()));
Log.d("left         :", String.valueOf(rectf.left));
Log.d("right        :", String.valueOf(rectf.right));
Log.d("top          :", String.valueOf(rectf.top));
Log.d("bottom       :", String.valueOf(rectf.bottom));

希望这会有所帮助

First you have to get the localVisible rectangle of the view

For eg:

Rect rectf = new Rect();

//For coordinates location relative to the parent
anyView.getLocalVisibleRect(rectf);

//For coordinates location relative to the screen/display
anyView.getGlobalVisibleRect(rectf);

Log.d("WIDTH        :", String.valueOf(rectf.width()));
Log.d("HEIGHT       :", String.valueOf(rectf.height()));
Log.d("left         :", String.valueOf(rectf.left));
Log.d("right        :", String.valueOf(rectf.right));
Log.d("top          :", String.valueOf(rectf.top));
Log.d("bottom       :", String.valueOf(rectf.bottom));

Hope this will help

初见终念 2024-08-27 07:32:53

使用全局布局侦听器对我来说一直很有效。它的优点是,如果布局发生更改,例如将某些内容设置为 View.GONE 或添加/删除子视图,则能够重新测量内容。

public void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);

     // inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
     LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null); 

     // set a global layout listener which will be called when the layout pass is completed and the view is drawn
     mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(
       new ViewTreeObserver.OnGlobalLayoutListener() {
          public void onGlobalLayout() {
               //Remove the listener before proceeding
               if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
               } else {
                    mainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
               }

               // measure your views here
          }
       }
     );

     setContentView(mainLayout);
 }

Using a global layout listener has always worked well for me. It has the advantage of being able to remeasure things if the layout is changed, e.g. if something is set to View.GONE or child views are added/removed.

public void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);

     // inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
     LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null); 

     // set a global layout listener which will be called when the layout pass is completed and the view is drawn
     mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(
       new ViewTreeObserver.OnGlobalLayoutListener() {
          public void onGlobalLayout() {
               //Remove the listener before proceeding
               if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
               } else {
                    mainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
               }

               // measure your views here
          }
       }
     );

     setContentView(mainLayout);
 }
娇纵 2024-08-27 07:32:53

第一种方式:

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

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() 获取绝对 X 并通过 view.absY() 获取绝对 Y

First Way:

In Kotlin we can create a simple extension for view:

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

And simply get coordinates:

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

Second Way:

The Second way is more simple :

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

and simply get absolute X by view.absX() and Y by view.absY()

策马西风 2024-08-27 07:32:53

根据 Romain Guy 的评论,我是如何解决这个问题的。希望它能帮助其他也遇到此问题的人。

我确实试图在视图显示在屏幕上之前获取视图的位置,但发生的情况一点也不明显。这些行是在初始化代码运行后放置的,所以我认为一切都准备好了。然而,这段代码仍然在 onCreate(); 中。通过尝试 Thread.sleep() 我发现布局实际上并没有最终确定,直到 onCreate() 一直到 onResume() 完成执行。事实上,代码试图在布局完成在屏幕上的定位之前运行。通过将代码添加到 OnClickListener(或其他监听器),可以获得正确的值,因为它只能在布局完成后触发。


下面的行被建议作为社区编辑:

请使用 onWindowfocuschanged(boolean hasFocus)

Following Romain Guy's comment, here's how I fixed it. Hopefully it'll help anyone else who also had this problem.

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.


The line below was suggested as a community edit:

please use onWindowfocuschanged(boolean hasFocus)

梦里南柯 2024-08-27 07:32:53

您可以使用 getLocationOnScreen( )getLocationInWindow()

之后,xy 应该是视图的左上角。如果您的根布局小于屏幕(例如在对话框中),则使用 getLocationInWindow 将相对于其容器,而不是整个屏幕。

Java解决方案

int[] point = new int[2];
view.getLocationOnScreen(point); // or getLocationInWindow(point)
int x = point[0];
int y = point[1];

注意:如果值始终为 0,您可能会在请求位置之前立即更改视图。

为了确保视图有机会更新,请在使用 view.post 计算视图的新布局后运行位置请求:

view.post(() -> {
    // Values should no longer be 0
    int[] point = new int[2];
    view.getLocationOnScreen(point); // or getLocationInWindow(point)
    int x = point[0];
    int y = point[1];
});

~~

Kotlin 解决方案

val point = IntArray(2)
view.getLocationOnScreen(point) // or getLocationInWindow(point)
val (x, y) = point

注意:如果值始终为 0,您可能会在请求位置之前立即更改视图。

为了确保视图有机会更新,请在使用 view.post 计算视图的新布局后运行您的位置请求:

view.post {
    // Values should no longer be 0
    val point = IntArray(2)
    view.getLocationOnScreen(point) // or getLocationInWindow(point)
    val (x, y) = point
}

我建议创建一个扩展函数来处理此问题:

// To use, call:
val (x, y) = view.screenLocation

val View.screenLocation get(): IntArray {
    val point = IntArray(2)
    getLocationOnScreen(point)
    return point
}

如果您需要可靠性,还添加:

view.screenLocationSafe { x, y -> Log.d("", "Use $x and $y here") }

fun View.screenLocationSafe(callback: (Int, Int) -> Unit) {
    post {
        val (x, y) = screenLocation
        callback(x, y)
    }
}

You can get a View's coordinates using getLocationOnScreen() or getLocationInWindow()

Afterwards, x and y should be the top-left corner of the view. If your root layout is smaller than the screen (like in a Dialog), using getLocationInWindow will be relative to its container, not the entire screen.

Java Solution

int[] point = new int[2];
view.getLocationOnScreen(point); // or getLocationInWindow(point)
int x = point[0];
int y = point[1];

NOTE: If value is always 0, you are likely changing the view immediately before requesting location.

To ensure view has had a chance to update, run your location request after the View's new layout has been calculated by using view.post:

view.post(() -> {
    // Values should no longer be 0
    int[] point = new int[2];
    view.getLocationOnScreen(point); // or getLocationInWindow(point)
    int x = point[0];
    int y = point[1];
});

~~

Kotlin Solution

val point = IntArray(2)
view.getLocationOnScreen(point) // or getLocationInWindow(point)
val (x, y) = point

NOTE: If value is always 0, you are likely changing the view immediately before requesting location.

To ensure view has had a chance to update, run your location request after the View's new layout has been calculated by using view.post:

view.post {
    // Values should no longer be 0
    val point = IntArray(2)
    view.getLocationOnScreen(point) // or getLocationInWindow(point)
    val (x, y) = point
}

I recommend creating an extension function for handling this:

// To use, call:
val (x, y) = view.screenLocation

val View.screenLocation get(): IntArray {
    val point = IntArray(2)
    getLocationOnScreen(point)
    return point
}

And if you require reliability, also add:

view.screenLocationSafe { x, y -> Log.d("", "Use $x and $y here") }

fun View.screenLocationSafe(callback: (Int, Int) -> Unit) {
    post {
        val (x, y) = screenLocation
        callback(x, y)
    }
}
没︽人懂的悲伤 2024-08-27 07:32:53

除了上述答案之外,对于您应该在何时何地调用 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];
    view.getLocationOnScreen(location);
    int x = location[0];
    int y = location[1];
  }
});

Just in addition to the above answers, for the question where and when you should call getLocationOnScreen?

For any information that is related to the view, will be available only after the view has been laid out (created) on the screen. So to get the location put your code inside view.post(Runnable) which is called after view has been laid out, like this:

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];
    view.getLocationOnScreen(location);
    int x = location[0];
    int y = location[1];
  }
});
红墙和绿瓦 2024-08-27 07:32:53

我的 utils 函数用于获取视图位置,它将返回一个带有 x 值y 值Point

public static Point getLocationOnScreen(View view){
    int[] location = new int[2];
    view.getLocationOnScreen(location);
    return new Point(location[0], location[1]);
}

对象

Point viewALocation = getLocationOnScreen(viewA);

My utils function for get view location, it will return a Point object with x value and y value

public static Point getLocationOnScreen(View view){
    int[] location = new int[2];
    view.getLocationOnScreen(location);
    return new Point(location[0], location[1]);
}

Using

Point viewALocation = getLocationOnScreen(viewA);
古镇旧梦 2024-08-27 07:32:53

在屏幕上获取视图位置和尺寸

val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;

    if (viewTreeObserver.isAlive) {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                //Remove Listener
                videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);
                
                //View Dimentions
                viewWidth = videoView.width;
                viewHeight = videoView.height;

                //View Location
                val point = IntArray(2)
                videoView.post {
                    videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
                    viewPositionX = point[0]
                    viewPositionY = point[1]
                }

            }
        });
    }

Get Both View Position and Dimension on screen

val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;

    if (viewTreeObserver.isAlive) {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                //Remove Listener
                videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);
                
                //View Dimentions
                viewWidth = videoView.width;
                viewHeight = videoView.height;

                //View Location
                val point = IntArray(2)
                videoView.post {
                    videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
                    viewPositionX = point[0]
                    viewPositionY = point[1]
                }

            }
        });
    }
夜夜流光相皎洁 2024-08-27 07:32:53
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 方式。
确保同时处理向上和向下操作,否则可能会导致多次单击/触摸。

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
        }

    })

This is the Kotlin way of getting coordinates of screen view when you touch the screen.
Make sure you handle both Action UP and DOWN or it may cause multiple click/touch.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文