getLocationOnScreen() vs getLocationInWindow()
Asked Answered
Y

4

73

What is the difference between screen and view in the context of these two methods?

I have a button and I want to get the x coordinate of its center.

I guess this would be enough:

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

but then, what difference would it make if I would have used

getLocationOnScreen() or getLocationInWindow() ?

(adding half of the button's width to that, of course)

Yasmeen answered 16/7, 2013 at 9:32 Comment(1)
With Android N and its multi-window support (developer.android.com/preview/features/multi-window.html) you should probably favor getLocationInWindow() over getLocationOnScreen() since the latter might lead to some unexpected behaviourUnexpressed
R
123

I don't think this answer is correct. If I create a new project, and edit only the MainActivity by adding the following snippet:

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

I will see printed to the console 108 108. This is using a Nexus 7 running 4.3. I have similar results using emulators running android versions as far back as 2.2.

Normal activity windows will have FILL_PARENTxFILL_PARENT as their WindowManager.LayoutParams, which results in them laying out to the size of the entire screen. The Window is laid out underneath (in regards to z-order, not y-coordinates) the statusbar and other decorations, so I believe a more accurate chart would be:

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

If you step through the source of these two methods, you will see that getLocationInWindow traverses up your view's view hierarchy up to the RootViewImpl, summing view coordinates and subtracting parent scroll offsets. In the case I described above the ViewRootImpl is getting the status bar height from the WindowSession, and passing it down through fitSystemWindows to the ActionBarOverlayLayout, which adds this value to the actionbar height. ActionBarOverlayLayout then takes this summed value and applies it to its content view, which is the parent of your layout, as a margin.

So, your content is laid out lower than the status bar not as a result of the window starting at a lower y coordinate than the status bar, but instead as a result of a margin being applied to your activity's content view.

If you peer into the getLocationOnScreen source you'll see it merely calls getLocationInWindow and then adds the Window's left and top coords (which are also passed to the View by ViewRootImpl, which fetches them from the WindowSession). In the normal case, these values will both be zero. There are some situations where these values may be non-zero, for example a dialog window that is placed in the middle of the screen.


So, to summarize: A normal activity's window fills the entire screen, even the space under the status bar and decorations. The two methods in question will return the same x and y coordinates. Only in special cases such as dialogs where the Window is actually offset will these two values differ.

Rabies answered 22/11, 2013 at 21:11 Comment(1)
Why does my Activity Window shrinks if I add on-screen Navigation Bar in my phone? It is also a decor like status bar.Bonnie
P
35

getLocationOnScreen() will get the location based on the phone screen.
getLocationInWindow() will get the location based on the activity window.

For the normal activity (not full-screen activity), the relation with phone screen and activity window is as shown below:

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

For the x coordinate, the value of both methods is usually the same.
For the y coordinate, the values have a difference for the status bar's height.

Pammie answered 16/7, 2013 at 10:6 Comment(1)
This answer is inconsistent with what I observe when creating an empty project and calling these two methods. I explained further in an answer below.Rabies
S
12

The current accepted answer is a little wordy. Here is a shorter one.

getLocationOnScreen() and getLocationInWindow() normally return the same values. This is because the window is normally the same size as the screen. However, sometimes the window is smaller than the screen. For example, in a Dialog or a custom system keyboard.

So if you know that the coordinates you want are always relative to the screen (like in a normal activity), then you can use getLocationOnScreen(). However, if your view is in a Window that might be smaller than the screen (like in a Dialog or custom keyboard), then use getLocationInWindow().

Related

Sphenic answered 31/3, 2018 at 9:52 Comment(0)
P
5

For getLocationOnScreen(), x and y will return the top-left corner of the view.

Using getLocationInWindow() will be relative to its container, not the entire screen. This will be different than getLocationOnScreen() if your root layout is smaller than the screen (like in a Dialog). For most cases they will be identical.

NOTE: If value is always 0, you are likely changing the view immediately before requesting location. You can use view.post to ensure the values are available

Java Solution

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

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

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:

// 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)
    }
}
Palpable answered 20/1, 2020 at 20:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.