454

I'm trying to get the absolute screen pixel coordinates of the top left corner of a view. However, all methods I can find such as getLeft() and getRight() don't work as they all seem to be relative to the parent of the view, thus giving me 0. What is the proper way to do this?

If it helps, this is for a 'put the picture back in order' game. I want the user to be able to draw a box to select multiple pieces. My assumption is that the easiest way to do that is to getRawX() and getRawY() from the MotionEvent and then compare those values against the top left corner of the layout holding the pieces. Knowing the size of the pieces, I can then determine how many pieces have been selected. I realise I can use getX() and getY() on the MotionEvent, but as that returns a relative position that makes determining which pieces were selected more difficult. (Not impossible, I know, but it seems unnecessarily complicated).

Edit: This is the code I used to try to get the size of the holding container, as per one of the questions. TableLayout is the table which holds all the puzzle pieces.

TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
Log.d(LOG_TAG, "Values " + tableLayout.getTop() + tableLayout.getLeft());

Edit 2: Here is the code I've tried, following more of the suggested answers.

public int[] tableLayoutCorners = new int[2];
(...)

TableLayout tableLayout = (TableLayout) findViewById(R.id.tableLayout);
tableLayout.requestLayout();
Rect corners = new Rect();
tableLayout.getLocalVisibleRect(corners);
Log.d(LOG_TAG, "Top left " + corners.top + ", " + corners.left + ", " + corners.right
            + ", " + corners.bottom);

cells[4].getLocationOnScreen(tableLayoutCorners);
Log.d(LOG_TAG, "Values " + tableLayoutCorners[0] + ", " + tableLayoutCorners[1]);

This code was added after all the initialisation is done. The image has been divided up into a array of ImageViews (the cells[] array) contained within a TableLayout. Cells[0] is the top left ImageView, and I picked cells[4] as it's somewhere in the middle and most definitely should not have coordinates of (0,0).

The code shown above still gives me all 0s in the logs, which I really don't understand because the various puzzle pieces are correctly displayed. (I tried public int for tableLayoutCorners and default visibility, both giving the same result.)

I don't know if this is significant, but the ImageViews are originally not given a size. The size of the ImageViews is determined during the initialisation automatically by the View when I give it an image to display. Could this contribute to their values being 0, even though that logging code is after they have been given an image and have automatically resized themselves? To potentially counter that, I added the code tableLayout.requestLayout() as shown above, but that didn't help.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
Steve Haley
  • 55,374
  • 17
  • 77
  • 85

11 Answers11

699

Use View.getLocationOnScreen() and/or getLocationInWindow().

TapanHP
  • 5,969
  • 6
  • 37
  • 66
Romain Guy
  • 97,993
  • 18
  • 219
  • 200
  • 3
    Now that's the kind of function I expected to find. Unfortunately, I must not be using it properly. I realise it was a known bug that getLocationOnScreen gives a null pointer exception in Android v1.5 (the platform I'm coding for), but testing in v2.1 didn't work any better. Both methods gave me 0s for everything. I included a code snippet above. – Steve Haley Feb 09 '10 at 12:23
  • 83
    You can only invoke it *AFTER* layout has happened. You are calling the method before the views are positioned on screen. – Romain Guy Feb 09 '10 at 19:33
  • 5
    Aha, you're right though it wasn't obvious that I was doing that. Thanks! For anyone curious for more details, I've added an answer which explains what I mean. – Steve Haley Feb 09 '10 at 21:10
  • @Steve - Yes, the ordering of calls that have to do w layout (because of these issues) is very important and can lead to some really annoying to find bugs. – Peter Ajtai Nov 17 '11 at 18:00
  • 1
    Now it would be awesome if the inflater or whoever is in charge, would offer a callback saying: Hey, I'm done, views are all positioned. – Martin Marconcini Jul 16 '13 at 00:52
  • 9
    You mean like the ViewTreeObserver and its OnGlobalLayoutListener? See View.getViewTreeObserver() to get started. – Romain Guy Jul 16 '13 at 19:13
  • 8
    @RomainGuy Yeah, kind of like that, but less confusing than having to register (and then unregister…). This is an area where iOS does it better than Android in terms of SDK. Using both on a daily basis, I can say iOS has a lot of annoying things, but UIView handling is not one of those. :) – Martin Marconcini Oct 09 '13 at 19:38
  • @RomainGuy Sorry if this is really late but, in which particular state of the `Activity` should those calculations been done? I get `{0, 0}` on the `onCreate()` but, if I put it in, let say `eventHandlerMethod()` I get it right? – mr5 Jun 08 '14 at 07:54
  • @mr5: See Simon's answer below. You will want to use the OnGlobalLayoutListener. That's by far the easiest way to make sure you're calling these after the values are ready. – lilbyrdie Jun 08 '14 at 18:36
  • Used this info with this one: http://stackoverflow.com/questions/7414065/android-scale-animation-on-view to animate scale around an absolute center - useful when translating a view and than animating it (for example shrink grow around new center). – benchuk Dec 25 '14 at 16:26
  • @RomainGuy, I have been facing the same issue as Stanly did, for some reason it gives me 0 in every method i try. It seems you have figured it out why is it giving zero. I am trying to get coordinates of view after the layout is loaded still i get zero why is that so? Thanks for your help, http://stackoverflow.com/questions/28622966/not-able-to-get-position-for-view-for-translate-animation-in-android – Pankaj Nimgade Feb 20 '15 at 07:21
  • @RomainGuy, how do I know when my layout is completely loaded – Pankaj Nimgade Feb 20 '15 at 07:22
  • 1
    But what is the difference between the two? – William Mar 26 '15 at 16:58
  • Will this still work if the view I am trying to locate does not have a parent? – Chad Bingham Jan 01 '16 at 20:48
  • One word of caution: When a view is rotated, this has inpact on the returned coordinates. I had a square image rotated by -90° and it seemed as if the returned location was that of the bottom-left corner. It took me quite some time to figure that one out. – H. de Jonge Oct 27 '16 at 15:13
  • @H.deJonge : did u get the answer for u r issue? – DKV Jan 02 '17 at 08:38
  • @RomainGuy : is it possible get the center after rotation and scaling – DKV Jan 02 '17 at 08:40
  • @VV I just wrapped it in another view and used that to read the dimensions. – H. de Jonge Jan 16 '17 at 09:24
  • I just noticed that getLocationOnScreen doesn't take rotation into account. So if you do view.setRotation(180f) then call getLocationOnScreen it will report top right coordinates instead of top left. – Nick Korostelev Nov 17 '17 at 01:49
  • Sorry, I meant to say that it will report **bottom right** coordinates instead of top left. – Nick Korostelev Nov 17 '17 at 02:15
  • this doesnt work for the views inside scroll view – Mohamad Rostami May 11 '21 at 07:17
188

The accepted answer didn't actually tell how to get the location, so here is a little more detail. You pass in an int array of length 2 and the values are replaced with the view's (x, y) coordinates (of the top, left corner).

int[] location = new int[2];
myView.getLocationOnScreen(location);
int x = location[0];
int y = location[1];

Notes

  • Replacing getLocationOnScreen with getLocationInWindow should give the same results in most cases (see this answer). However, if you are in a smaller window like a Dialog or custom keyboard, then use you will need to choose which one better suits your needs.
  • You will get (0,0) if you call this method in onCreate because the view has not been laid out yet. You can use a ViewTreeObserver to listen for when the layout is done and you can get the measured coordinates. (See this answer.)
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
112

First you have to get the localVisible rectangle of the view

For eg:

Rect rectf = new Rect();

//For coordinates location relative to the parent
anyView.getLocalVisibleRect(rectf);

//For coordinates location relative to the screen/display
anyView.getGlobalVisibleRect(rectf);

Log.d("WIDTH        :", String.valueOf(rectf.width()));
Log.d("HEIGHT       :", String.valueOf(rectf.height()));
Log.d("left         :", String.valueOf(rectf.left));
Log.d("right        :", String.valueOf(rectf.right));
Log.d("top          :", String.valueOf(rectf.top));
Log.d("bottom       :", String.valueOf(rectf.bottom));

Hope this will help

Lavekush Agrawal
  • 6,040
  • 7
  • 52
  • 85
Naveen Murthy
  • 3,661
  • 2
  • 21
  • 22
  • 4
    That also should have worked... However it's giving me values of 0 as well. I included a code snippet above if that helps you figure out what I did wrong. Thanks again. – Steve Haley Feb 09 '10 at 12:24
  • 1
    See my other comments; I was accidentally calling this before the Views had finished laying themselves out. This works fine now thanks. – Steve Haley Feb 09 '10 at 21:17
  • @Naveen Murthy it gives coordinates to specific view, i need coordinates of clicked view in root layout.. – Aniket Jun 18 '13 at 16:09
  • 22
    this returns the location relative to the parent. use `getGlobalVisibleRect()` for absolute coords. – Jeffrey Blattman Apr 12 '14 at 01:59
  • 3
    @SteveHaley, I have been facing the same issue as you did, for some reason it gives me 0 in every method i try. It seems you have figured it out why is it giving zero. I am trying to get coordinates of view after the layout is loaded still i get zero why is that so? Thanks for your help – Pankaj Nimgade Feb 20 '15 at 07:02
  • he return rect of android.R.id.content. how i can get rect of decorview? –  Mar 17 '18 at 09:57
  • It always returns zeros. At the same time `.getLocationOnScreen()` works. But I also want to know the width. – Dmitry Sep 23 '18 at 00:32
  • This 9.5 year old answer works flawlessly. I needed those 4 coordinates top-left-bottom-right which it provides, along with width and height. Thanks! – Rohit Kumar Jul 27 '19 at 10:25
51

Using a global layout listener has always worked well for me. It has the advantage of being able to remeasure things if the layout is changed, e.g. if something is set to View.GONE or child views are added/removed.

public void onCreate(Bundle savedInstanceState)
{
     super.onCreate(savedInstanceState);

     // inflate your main layout here (use RelativeLayout or whatever your root ViewGroup type is
     LinearLayout mainLayout = (LinearLayout ) this.getLayoutInflater().inflate(R.layout.main, null); 

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

               // measure your views here
          }
       }
     );

     setContentView(mainLayout);
 }
Siwei
  • 19,858
  • 7
  • 75
  • 95
Simon
  • 14,407
  • 8
  • 46
  • 61
  • Don't forget to remove the OnGlobalLayoutListener inside of (at the end of) the onGlobalLayout(){...}. Also, just to be sure, check if what'\s passed != 0; the listener can be called twice, once with a value of w=0/h=0, the second time with the actual, correct, values. – MacD Jul 02 '13 at 14:27
  • This way to inflate the main layout and then use `setContentView()` caused a problem in a special `Activity` in my app! That was a dialog activity (`windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true`). Its main layout (a `LinearLayout`) hasn't any direct child with determined `layout_width` (All of them were `match_parent` or `wrap_content`). – Mir-Ismaili Dec 16 '17 at 05:45
28

First Way:

In Kotlin we can create a simple extension for view:

fun View.getLocationOnScreen(): Point
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return Point(location[0],location[1])
}

And simply get coordinates:

val location = yourView.getLocationOnScreen()
val absX = location.x
val absY = location.y

Second Way:

The Second way is more simple :

fun View.absX(): Int
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return location[0]
}

fun View.absY(): Int
{
    val location = IntArray(2)
    this.getLocationOnScreen(location)
    return location[1]
}

and simply get absolute X by view.absX() and Y by view.absY()

Amir Hossein Ghasemi
  • 20,623
  • 10
  • 57
  • 53
19

Following Romain Guy's comment, here's how I fixed it. Hopefully it'll help anyone else who also had this problem.

I was indeed trying to get the positions of the views before they had been laid out on the screen but it wasn't at all obvious that was happening. Those lines had been placed after the initilisation code ran, so I assumed everything was ready. However, this code was still in onCreate(); by experimenting with Thread.sleep() I discovered that the layout is not actually finalised until after onCreate() all the way to onResume() had finished executing. So indeed, the code was trying to run before the layout had finished being positioned on the screen. By adding the code to an OnClickListener (or some other Listener) the correct values were obtained because it could only be fired after the layout had finished.


The line below was suggested as a community edit:

please use onWindowfocuschanged(boolean hasFocus)

Adi Inbar
  • 12,097
  • 13
  • 56
  • 69
Steve Haley
  • 55,374
  • 17
  • 77
  • 85
  • So you mean to say that setContentView(my_layout); will not place all the views. All the views will be placed only after onCreate() is finished? – Ashwin Sep 22 '12 at 13:01
  • 5
    Not quite. All the views 'exist' after `setContentView(R.layout.something)`, but they haven't been loaded up visually. They don't have a size, or a position. That doesn't happen until the first layout pass finishes, which happens at some point in the future (generally after onResume()). – Steve Haley Sep 24 '12 at 16:40
17

You can get a View's coordinates using getLocationOnScreen() or getLocationInWindow()

Afterwards, x and y should be the top-left corner of the view. If your root layout is smaller than the screen (like in a Dialog), using getLocationInWindow will be relative to its container, not the entire screen.

Java Solution

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

NOTE: If value is always 0, you are likely changing the view immediately before requesting location.

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

NOTE: If value is always 0, you are likely changing the view immediately before requesting location.

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:

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)
    }
}
Gibolt
  • 42,564
  • 15
  • 187
  • 127
12

Just in addition to the above answers, for the question where and when you should call getLocationOnScreen?

For any information that is related to the view, will be available only after the view has been laid out (created) on the screen. So to get the location put your code inside view.post(Runnable) which is called after view has been laid out, like this:

view.post(new Runnable() {
  @Override
  public void run() {
    // This code will run when view created and rendered on screen
    // So as the answer to this question, you can put the code here
    
    int[] location = new int[2];
    view.getLocationOnScreen(location);
    int x = location[0];
    int y = location[1];
  }
});
mortalis
  • 2,060
  • 24
  • 34
Suraj Vaishnav
  • 7,777
  • 4
  • 43
  • 46
6

My utils function for get view location, it will return a Point object with x value and y value

public static Point getLocationOnScreen(View view){
    int[] location = new int[2];
    view.getLocationOnScreen(location);
    return new Point(location[0], location[1]);
}

Using

Point viewALocation = getLocationOnScreen(viewA);
Linh
  • 57,942
  • 23
  • 262
  • 279
0

Get Both View Position and Dimension on screen

val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;

    if (viewTreeObserver.isAlive) {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                //Remove Listener
                videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);
                
                //View Dimentions
                viewWidth = videoView.width;
                viewHeight = videoView.height;

                //View Location
                val point = IntArray(2)
                videoView.post {
                    videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
                    viewPositionX = point[0]
                    viewPositionY = point[1]
                }

            }
        });
    }
Community
  • 1
  • 1
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
0
binding.ivStory.setOnTouchListener(object : View.OnTouchListener{
        override fun onTouch(p0: View?, event: MotionEvent?): Boolean {
            val x = event?.x
            vak y = event?.y

            when (event!!.action) {
                MotionEvent.ACTION_UP ->

                if (x != null) {
                   //Do your stuff                    }

                MotionEvent.ACTION_DOWN  -> {

                }
            }
            return true
        }

    })

This is the Kotlin way of getting coordinates of screen view when you touch the screen. Make sure you handle both Action UP and DOWN or it may cause multiple click/touch.