getLocationOnScreen()vs getLocationInWindow()

时间:2013-07-16 09:32:03

标签: java android view screen

在这两种方法的背景下,屏幕和视图有什么区别?

我有一个按钮,我想获得其中心的x坐标。

我想这就足够了:

public int getButtonXPosition() {
    return (button.getLeft()+button.getRight())/2;
}

但是,如果我使用

会有什么不同

getLocationOnScreen()getLocationInWindow()

(当然,加上按钮宽度的一半)

4 个答案:

答案 0 :(得分:107)

我认为this answer不正确。如果我创建一个新项目,并通过添加以下代码段仅编辑MainActivity:

public boolean dispatchTouchEvent(MotionEvent ev) {
    View contentsView = findViewById(android.R.id.content);

    int test1[] = new int[2];
    contentsView.getLocationInWindow(test1);

    int test2[] = new int[2];
    contentsView.getLocationOnScreen(test2);

    System.out.println(test1[1] + " " + test2[1]);

    return super.dispatchTouchEvent(ev);
}

我会看到打印到控制台108 108。这是使用运行4.3的Nexus 7。我使用运行Android版本的模拟器的结果可以追溯到2.2。

正常活动窗口将使用FILL_PARENTxFILL_PARENT作为其WindowManager.LayoutParams,这将导致它们布局为整个屏幕的大小。窗口布局在下面(关于z顺序,而不是y坐标)状态栏和其他装饰,所以我相信更精确的图表将是:

|--phone screen-----activity window---| 
|--------status bar-------------------| 
|                                     | 
|                                     | 
|-------------------------------------| 

如果您逐步浏览这两种方法的来源,您会看到getLocationInWindow遍历视图的视图层次结构,直到RootViewImpl,求和视图坐标和 减去父滚动偏移量。在上面描述的情况下,ViewRootImpl从WindowSession获取状态栏高度,并通过fitSystemWindows向下传递给ActionBarOverlayLayout,后者将此值添加到操作栏高度。然后ActionBarOverlayLayout获取此求和值并将其作为边距应用于其内容视图(布局的父级)。

因此,您的内容布局低于状态栏,而不是因为窗口从较低的y坐标开始而不是状态栏,而是由于边距应用于您的活动的内容视图。< / p>

如果您查看getLocationOnScreen来源,您会看到它仅调用getLocationInWindow,然后添加Window的左侧和顶部坐标(它们也通过ViewRootImpl传递给View,它从中获取WindowSession)。在正常情况下,这些值都将为零。在某些情况下,这些值可能不为零,例如放置在屏幕中间的对话框窗口。


因此,总结一下:正常活动的窗口填满整个屏幕,甚至是状态栏和装饰下的空间。有问题的两种方法将返回相同的x和y坐标。只有在窗口实际偏移的对话框等特殊情况下,这两个值才会不同。

答案 1 :(得分:23)

getLocationOnScreen()将根据手机屏幕获取位置。

getLocationInWindow()将根据活动窗口获取位置。

对于正常活动(非全屏活动),与电话屏幕和活动窗口的关系如下:

  

| - 手机屏幕 --------------------- |
  | -------- 状态栏 --------------------- |
  | |
  | ------------------------------------------- |
  | -------- 活动窗口 ------ ------- |
  | |
  | |
  | |
  | |
  | |
  | |
    | ------------------------------------------- |

对于x坐标,两种方法的值通常相同。

对于y坐标,值与状态栏的高度

有所不同

答案 2 :(得分:2)

目前接受的答案有点罗嗦。这是一个较短的。

getLocationOnScreen()getLocationInWindow()通常会返回相同的值。这是因为窗口通常与屏幕大小相同。但是,有时窗口小于屏幕。例如,在Dialog或自定义系统键盘中。

因此,如果您知道所需的坐标始终与屏幕相关(就像在正常活动中一样),那么您可以使用getLocationOnScreen()。但是,如果您的视图位于可能小于屏幕的窗口中(例如在对话框或自定义键盘中),则使用getLocationInWindow()

相关

答案 3 :(得分:1)

对于getLocationOnScreen()xy将返回视图的左上角。

使用getLocationInWindow()相对于其容器,而不是整个屏幕。如果您的根目录布局小于屏幕(例如在对话框中),则此设置将与getLocationOnScreen()不同。在大多数情况下,它们是相同的。

注意::如果值始终为0,则可能会在请求位置之前立即更改视图。您可以使用 view.post 确保值可用

Java解决方案

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

为确保视图有机会更新,请在使用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];
});

~~

科特林溶液

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

为确保视图有机会更新,请在使用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
}

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

// To use, call:
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)
    }
}