【问题标题】:getLocationOnScreen() vs getLocationInWindow()getLocationOnScreen() 与 getLocationInWindow()
【发布时间】:2013-07-14 10:02:32
【问题描述】:

这两种方法的上下文中screen和view有什么区别?

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

我想这就够了:

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

但是,如果我使用它会有什么不同

getLocationOnScreen()getLocationInWindow() ?

(当然是按钮宽度的一半)

【问题讨论】:

标签: java android view screen


【解决方案1】:

我不认为this answer 是正确的。如果我创建一个新项目,并通过添加以下 sn-p 仅编辑 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 坐标开始,而是因为您的活动的内容视图应用了边距。

如果您查看getLocationOnScreen 源,您会看到它只是调用getLocationInWindow,然后添加窗口的左坐标和上坐标(它们也由 ViewRootImpl 传递给视图,它从 WindowSession 中获取它们)。在正常情况下,这些值都为零。在某些情况下,这些值可能不为零,例如位于屏幕中间的对话窗口。


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

【讨论】:

  • 如果我在手机中添加屏幕导航栏,为什么我的活动窗口会缩小?它也是一个类似于状态栏的装饰。
【解决方案2】:

getLocationOnScreen() 将根据手机屏幕获取位置。
getLocationInWindow() 将根据活动窗口获取位置。

普通活动(非全屏活动),手机屏幕和活动窗口的关系如下图:

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

对于x坐标,这两种方法的值通常是相同的。
对于y 坐标,状态栏的高度值不同。

【讨论】:

  • 这个答案与我在创建一个空项目并调用这两个方法时观察到的不一致。我在下面的答案中进一步解释了。
【解决方案3】:

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

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

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

相关

【讨论】:

    【解决方案4】:

    对于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];
    });
    

    ~~

    Kotlin 解决方案

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-07
      • 1970-01-01
      • 2019-12-03
      • 2014-12-16
      • 2016-03-23
      • 1970-01-01
      • 1970-01-01
      • 2012-12-05
      相关资源
      最近更新 更多