Getting View's coordinates relative to the root layout
Asked Answered
H

11

156

Can I get a View's x and y position relative to the root layout of my Activity in Android?

Hoseahoseia answered 1/9, 2010 at 15:28 Comment(0)
U
149

This is one solution, though since APIs change over time and there may be other ways of doing it, make sure to check the other answers. One claims to be faster, and another claims to be easier.

private int getRelativeLeft(View myView) {
    if (myView.getParent() == myView.getRootView())
        return myView.getLeft();
    else
        return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}

private int getRelativeTop(View myView) {
    if (myView.getParent() == myView.getRootView())
        return myView.getTop();
    else
        return myView.getTop() + getRelativeTop((View) myView.getParent());
}

Let me know if that works.

It should recursively just add the top and left positions from each parent container. You could also implement it with a Point if you wanted.

Utta answered 1/9, 2010 at 18:30 Comment(14)
getRelativeLeft() this is need add cast,but when i add (View) or (button) ((Object) myView.getParent()).getRelativeTop(),it is also not rightClew
I did something very similar but i checked for the root view by getId == R.id.myRootView.Hoseahoseia
Log.i("RelativeLeft", ""+getRelativeLeft(findViewById(R.id.tv))); Log.i("RelativeTop", ""+getRelativeTop(findViewById(R.id.tv))); i get all is 0; textView in layout is as follows <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" android:id="@+id/tv" android:layout_marginBottom="69dip" android:layout_marginLeft="69dip" />Clew
Smiliar approach like this (calculating it manually) worked for me so I accepted this answer.Hoseahoseia
There is a chance that has something to do with the variable you're passing in.Utta
getParent return's a ViewGroup not a View so no idea how this worked for you...Solubilize
@Solubilize propose an edit if it needs a cast or another method.Utta
It doesn't need a cast or another method - AFAIK you can't find a View's position through recursion through the hierarchy of view's... The correct answer is "View.getLocationInWindow()`Solubilize
Interesting, this returned the same results for me as getLocationOnScreen() which also returned the same as getLocationInWindow(). Nonetheless, you can specify a parent in the argument to end the recursion, aka, get the xy relative to a parentFirenew
the android api already provides the relative coordinate to the root. look here https://mcmap.net/q/58142/-getting-view-39-s-coordinates-relative-to-the-root-layoutDepository
Every time i am getting return value 0 from both method.I have a one TextView inside RelativeLayout.Overdone
I got the solution, call both method form onWindowFocusChanged(boolean hasFocus) Activity class method.I think both method will only work when your Activity is visible to the user.Overdone
OR change the method signature to use your own rootView if you want to get the left,top corner relative to a given parent view. I.E. private int getRelativeLeft(View myView, View rootView) { if (myView.getParent() == rootView)Lillie
I always get 0. Please help.Quasi
D
219

The Android API already provides a method to achieve that. Try this:

Rect offsetViewBounds = new Rect();
//returns the visible bounds
childView.getDrawingRect(offsetViewBounds);
// calculates the relative coordinates to the parent
parentViewGroup.offsetDescendantRectToMyCoords(childView, offsetViewBounds); 

int relativeTop = offsetViewBounds.top;
int relativeLeft = offsetViewBounds.left;

Here is the doc

Depository answered 20/4, 2016 at 10:0 Comment(7)
This should be the accepted answer. No possibility of a stack overflow, and no possibility of math errors. Thanks!Slowly
I wanted to find the coordinates of a view in the toolbar. This is the one method that works consistently to account for android:fitsSystemWindows and various API versions from 19 to 27. So thank you! This IS the right answer.Rarefy
Worked very well, just want to mention that parentViewGroup can be any parent in the view hierarchy, so it works perfectly for ScrollView as well.Eyelid
I noticed that I get the same coordinates from this whether the view has been translated or not. This screwed up a calculation because it didn't account for the view being translated.Have you run into issues like that before?Enthymeme
This does not work: offsetTop is always zero on my device. I used view .getParent() for parentViewGroupSepulture
Man, this is SWWEEEEEEET!!! Thank you! For those getting zero, make sure your ViewGroup is a direct parent of the view you need to acquire the relative coordinates for.Krute
Yeah, this provides a more accurate coordinate valuesDevito
U
149

This is one solution, though since APIs change over time and there may be other ways of doing it, make sure to check the other answers. One claims to be faster, and another claims to be easier.

private int getRelativeLeft(View myView) {
    if (myView.getParent() == myView.getRootView())
        return myView.getLeft();
    else
        return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}

private int getRelativeTop(View myView) {
    if (myView.getParent() == myView.getRootView())
        return myView.getTop();
    else
        return myView.getTop() + getRelativeTop((View) myView.getParent());
}

Let me know if that works.

It should recursively just add the top and left positions from each parent container. You could also implement it with a Point if you wanted.

Utta answered 1/9, 2010 at 18:30 Comment(14)
getRelativeLeft() this is need add cast,but when i add (View) or (button) ((Object) myView.getParent()).getRelativeTop(),it is also not rightClew
I did something very similar but i checked for the root view by getId == R.id.myRootView.Hoseahoseia
Log.i("RelativeLeft", ""+getRelativeLeft(findViewById(R.id.tv))); Log.i("RelativeTop", ""+getRelativeTop(findViewById(R.id.tv))); i get all is 0; textView in layout is as follows <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" android:id="@+id/tv" android:layout_marginBottom="69dip" android:layout_marginLeft="69dip" />Clew
Smiliar approach like this (calculating it manually) worked for me so I accepted this answer.Hoseahoseia
There is a chance that has something to do with the variable you're passing in.Utta
getParent return's a ViewGroup not a View so no idea how this worked for you...Solubilize
@Solubilize propose an edit if it needs a cast or another method.Utta
It doesn't need a cast or another method - AFAIK you can't find a View's position through recursion through the hierarchy of view's... The correct answer is "View.getLocationInWindow()`Solubilize
Interesting, this returned the same results for me as getLocationOnScreen() which also returned the same as getLocationInWindow(). Nonetheless, you can specify a parent in the argument to end the recursion, aka, get the xy relative to a parentFirenew
the android api already provides the relative coordinate to the root. look here https://mcmap.net/q/58142/-getting-view-39-s-coordinates-relative-to-the-root-layoutDepository
Every time i am getting return value 0 from both method.I have a one TextView inside RelativeLayout.Overdone
I got the solution, call both method form onWindowFocusChanged(boolean hasFocus) Activity class method.I think both method will only work when your Activity is visible to the user.Overdone
OR change the method signature to use your own rootView if you want to get the left,top corner relative to a given parent view. I.E. private int getRelativeLeft(View myView, View rootView) { if (myView.getParent() == rootView)Lillie
I always get 0. Please help.Quasi
T
98

Please use view.getLocationOnScreen(int[] location); (see Javadocs). The answer is in the integer array (x = location[0] and y = location[1]).

Teage answered 23/7, 2011 at 3:46 Comment(10)
This returns position rlative to the screen not to the root layout of Activity.Hoseahoseia
There is also View.getLocationInWindow(int[]) which returns the view's position relative to the window.Counterglow
Can you please tell me exactly how this method work. It returns a void how to do we get the output or x, y values of a view on the screenCapreolate
To get the position relative to the root layout, just call getLocationInWindow for the root layout and the element, and use the power of subtraction. It's far simpler and likely faster than the accepted answer.Mesothorium
I think this is a really useful answer, though maybe it could be edited to give viewers of this question an easy way to get the answer they're looking for.Utta
This is a crazy old post, but don't capitalize View because getLocationInWindow is not a static methodDumah
Both getLocationOnScreen and getGlobalVisibleRect include the status bar height.Laellaertes
this is just the location related to the screen. not the the root. read here for relative to root: https://mcmap.net/q/58142/-getting-view-39-s-coordinates-relative-to-the-root-layoutDepository
what is the view is scaled and rotated?Rutabaga
Be aware that the location on screen may work when the app is running full screen on an Android device, but will be different if your app is running in a window on Chrome OS. This may not be the desired location measurement.Alarice
M
42
View rootLayout = view.getRootView().findViewById(android.R.id.content);

int[] viewLocation = new int[2]; 
view.getLocationInWindow(viewLocation);

int[] rootLocation = new int[2];
rootLayout.getLocationInWindow(rootLocation);

int relativeLeft = viewLocation[0] - rootLocation[0];
int relativeTop  = viewLocation[1] - rootLocation[1];

First I get the root layout then calculate the coordinates difference with the view.
You can also use the getLocationOnScreen() instead of getLocationInWindow().

Massotherapy answered 4/4, 2014 at 15:13 Comment(3)
what is the view is scaled and rotated?Rutabaga
This is the only answer which is working for me. This should be accepted answerSepulture
@Rutabaga Did you get solution regarding scale and rotation? I am also looking for the solution.Nirvana
R
38

No need to calculate it manually.

Just use getGlobalVisibleRect like so:

Rect myViewRect = new Rect();
myView.getGlobalVisibleRect(myViewRect);
float x = myViewRect.left;
float y = myViewRect.top;

Also note that for the centre coordinates, rather than something like:

...
float two = (float) 2
float cx = myViewRect.left + myView.getWidth() / two;
float cy = myViewRect.top + myView.getHeight() / two;

You can just do:

float cx = myViewRect.exactCenterX();
float cy = myViewRect.exactCenterY();
Riane answered 17/1, 2012 at 16:52 Comment(5)
Looking at the android source all getGlobalVisibleRect does is globalOffset.set(-mScrollX, -mScrollY); so you could just get those values with getScollX/Y if you only need the relative coodinates.Phooey
This should be accepted answer. getLocationOnScreen works as well as getGlobalVisibleRect. But getGlobalVisibleRect provides a bit more information's and there is no need to work with raw arrays. I would therefore go with getGlobalVisibleRect. The usage is simple. Rect positionRect = new Rect(); view.getGlobablVisibleRect(positionRect); //x = positionRect.left //y = positionRect.top. cheersWhatley
if some part of the child is outside which will not take into consideration in this case. how to solve such case?Rutabaga
Great answer! Thank you so much!Molnar
Anyone, @Whatley can you please answer this? #68869278Emeric
E
8

You can use `

view.getLocationOnScreen(int[] location)

;` to get location of your view correctly.

But there is a catch if you use it before layout has been inflated you will get wrong position.

Solution to this problem is adding ViewTreeObserver like this :-

Declare globally the array to store x y position of your view

 int[] img_coordinates = new int[2];

and then add ViewTreeObserver on your parent layout to get callback for layout inflation and only then fetch position of view otherwise you will get wrong x y coordinates

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

                            // measure your views here
                            fab.getLocationOnScreen(img_coordinates);
                        }
                    }
            );

and then use it like this

xposition = img_coordinates[0];
yposition =  img_coordinates[1];
Emotionalize answered 6/10, 2016 at 5:22 Comment(0)
S
4

I wrote myself two utility methods that seem to work in most conditions, handling scroll, translation and scaling, but not rotation. I did this after trying to use offsetDescendantRectToMyCoords() in the framework, which had inconsistent accuracy. It worked in some cases but gave wrong results in others.

"point" is a float array with two elements (the x & y coordinates), "ancestor" is a viewgroup somewhere above the "descendant" in the tree hierarchy.

First a method that goes from descendant coordinates to ancestor:

public static void transformToAncestor(float[] point, final View ancestor, final View descendant) {
    final float scrollX = descendant.getScrollX();
    final float scrollY = descendant.getScrollY();
    final float left = descendant.getLeft();
    final float top = descendant.getTop();
    final float px = descendant.getPivotX();
    final float py = descendant.getPivotY();
    final float tx = descendant.getTranslationX();
    final float ty = descendant.getTranslationY();
    final float sx = descendant.getScaleX();
    final float sy = descendant.getScaleY();

    point[0] = left + px + (point[0] - px) * sx + tx - scrollX;
    point[1] = top + py + (point[1] - py) * sy + ty - scrollY;

    ViewParent parent = descendant.getParent();
    if (descendant != ancestor && parent != ancestor && parent instanceof View) {
        transformToAncestor(point, ancestor, (View) parent);
    }
}

Next the inverse, from ancestor to descendant:

public static void transformToDescendant(float[] point, final View ancestor, final View descendant) {
    ViewParent parent = descendant.getParent();
    if (descendant != ancestor && parent != ancestor && parent instanceof View) {
        transformToDescendant(point, ancestor, (View) parent);
    }

    final float scrollX = descendant.getScrollX();
    final float scrollY = descendant.getScrollY();
    final float left = descendant.getLeft();
    final float top = descendant.getTop();
    final float px = descendant.getPivotX();
    final float py = descendant.getPivotY();
    final float tx = descendant.getTranslationX();
    final float ty = descendant.getTranslationY();
    final float sx = descendant.getScaleX();
    final float sy = descendant.getScaleY();

    point[0] = px + (point[0] + scrollX - left - tx - px) / sx;
    point[1] = py + (point[1] + scrollY - top - ty - py) / sy;
}
Skit answered 21/4, 2016 at 8:48 Comment(0)
S
2

Incase someone is still trying to figure this out. This is how you get the center X and Y of the view.

    int pos[] = new int[2];
    view.getLocationOnScreen(pos);
    int centerX = pos[0] + view.getMeasuredWidth() / 2;
    int centerY = pos[1] + view.getMeasuredHeight() / 2;
Stith answered 2/6, 2017 at 15:8 Comment(1)
THis doesn't work in my case ... please look at the question if you can solve it. #68869278Emeric
P
1

I just found the answer here

It says: It is possible to retrieve the location of a view by invoking the methods getLeft() and getTop(). The former returns the left, or X, coordinate of the rectangle representing the view. The latter returns the top, or Y, coordinate of the rectangle representing the view. These methods both return the location of the view relative to its parent. For instance, when getLeft() returns 20, that means the view is located 20 pixels to the right of the left edge of its direct parent.

so use:

view.getLeft(); // to get the location of X from left to right
view.getRight()+; // to get the location of Y from right to left
Protean answered 15/9, 2015 at 10:24 Comment(2)
This gives the location relative to the immediate parent of the view, not to the root view, which could be the parent of the parent etc...Laellaertes
what is the view is scaled and rotated?Rutabaga
M
1

You can use the following the get the difference between parent and the view you interested in:

private int getRelativeTop(View view) {
    final View parent = (View) view.getParent();
    int[] parentLocation = new int[2];
    int[] viewLocation = new int[2];

    view.getLocationOnScreen(viewLocation);
    parent.getLocationOnScreen(parentLocation);

    return viewLocation[1] - parentLocation[1];
}

Dont forget to call it after the view is drawn:

 timeIndicator.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
        final int relativeTop = getRelativeTop(timeIndicator);
    });
Medius answered 8/8, 2020 at 11:19 Comment(0)
P
0

I think it's best if you define the parent you wish to check, and if you want the root, choose it.

For this, I've made a simple, general function:

fun getPositionRelativeToParent(view: View, parentToReach: View): Point {
    var currentView: View? = view.parent as View?
    val result = Point(view.left, view.top)
    while (true) {
        if (currentView == null || currentView == parentToReach)
            break
        result.set(result.x + currentView.left, result.y + currentView.top)
        currentView = currentView.parent as View?
    }
    return result
}

Note that you need to use it after the Views found where to be positioned.

Here's an example. Suppose you have 2 Views. One is deep inside, and the other is a child of a common parent. Both have the same size and need to be positioned, one on top of the other.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val mainContainer = findViewById<View>(R.id.mainContainer)
        val viewToInspect = findViewById<View>(R.id.viewToInspect)
        val viewToMove = findViewById<View>(R.id.viewToMove)
        findViewById<View>(android.R.id.content).doOnPreDraw {
            val pos = getPositionRelativeToParent(viewToInspect, mainContainer)
            Log.d("AppLog", "getPositionRelativeToParent:$pos")
            viewToMove.updateLayoutParams<MarginLayoutParams> {
                leftMargin=pos.x
                topMargin=pos.y
            }
        }
    }
}

XML:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainContainer" android:layout_width="match_parent"
    android:layout_height="match_parent" tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent" android:layout_height="wrap_content"
        android:layout_margin="10dp" android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:layout_width="100dp" android:layout_height="100dp" android:gravity="center"
            android:text="1" />

        <TextView
            android:id="@+id/viewToInspect" android:layout_width="100dp" android:layout_height="100dp"
            android:background="#ff00ff00" android:gravity="center_horizontal" android:text="2"
            android:textColor="#00f" />

    </LinearLayout>

    <TextView
        android:id="@+id/viewToMove" android:layout_width="100dp" android:layout_height="100dp"
        android:background="#66ff0000" android:gravity="center" android:text="3"
        android:textColor="#0ff" />
</FrameLayout>

You can see that one View is on top of the other.

Paradise answered 6/3, 2023 at 13:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.