在这两种方法的背景下,屏幕和视图有什么区别?
我有一个按钮,我想获得其中心的x坐标。
我想这就足够了:
public int getButtonXPosition() {
return (button.getLeft()+button.getRight())/2;
}
但是,如果我使用
会有什么不同 getLocationOnScreen()
或getLocationInWindow()
?
(当然,加上按钮宽度的一半)
答案 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()
,x
和y
将返回视图的左上角。
使用getLocationInWindow()
相对于其容器,而不是整个屏幕。如果您的根目录布局小于屏幕(例如在对话框中),则此设置将与getLocationOnScreen()
不同。在大多数情况下,它们是相同的。
注意::如果值始终为0,则可能会在请求位置之前立即更改视图。您可以使用 view.post 确保值可用
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)
}
}