如何获取视图的绝对坐标


390

我正在尝试获取视图左上角的绝对屏幕像素坐标。但是,我可以找到的所有方法,例如getLeft()getRight()都不起作用,因为它们似乎都与视图的父项有关,因此给了我0。正确的方法是什么?

如果有帮助,则用于“按顺序放回图片”游戏。我希望用户能够绘制一个框来选择多个片段。我的假设是,做到这一点的最简单方法是getRawX(),并getRawY()MotionEvent再与之比较的布局保持件的左上角的值。知道了碎片的大小之后,我可以确定选择了多少碎片。我意识到我可以在上使用getX()和,但是那样会返回一个相对位置,这使得确定选择哪些作品更加困难。(我知道这不是没有可能,但似乎不必要地复杂)。getY()MotionEvent

编辑:根据问题之一,这是我用来尝试获取保存容器大小的代码。TableLayout是容纳所有拼图的桌子。

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

编辑2:这是我尝试过的代码,遵循更多建议的答案。

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

完成所有初始化后,便添加了此代码。图像已分为ImageViews数组(cells []数组),该数组包含在中TableLayout。Cells [0]位于左上角ImageView,我选择了cells [4],因为它位于中间的某个位置,并且绝对不应该具有(0,0)的坐标。

上面显示的代码仍然为我提供了日志中的全0,我真的不明白,因为正确显示了各个拼图。(我尝试使用public int来获取tableLayoutCorners和默认可见性,两者的结果相同。)

我不知道这是否有意义,但ImageView最初并未指定s的大小。ImageViews 的大小是在初始化期间由View自动给我显示图像时确定的。即使该日志记录代码是在给了它们图像并自动调整大小之后,这是否也会使它们的值为0?为了解决这个问题,我添加了tableLayout.requestLayout()上面所示的代码,但这没有帮助。

Answers:


632

3
现在,这就是我期望找到的功能。不幸的是,我一定不能正确使用它。我意识到这是一个已知的错误,getLocationOnScreen在Android v1.5(我正在为其编码的平台)中给出了空指针异常,但是在v2.1中进行测试并没有取得更好的效果。两种方法都使我得到0。我在上面包含了一个代码段。
史蒂夫·哈利

76
您只能在布局发生调用它。您正在视图放置在屏幕上之前调用该方法。
罗曼·盖伊

5
啊哈,您说得对,尽管我这样做不是很明显。谢谢!对于想了解更多详细信息的人,我添加了一个答案,解释了我的意思。
史蒂夫·哈利

9
您是说像ViewTreeObserver及其OnGlobalLayoutListener吗?请参阅View.getViewTreeObserver()入门。
罗曼·盖伊

8
@RomainGuy是的,有点像,但是比必须注册(然后注销…)要少。就SDK而言,iOS在这方面要比Android更好。我可以说每天都使用这两种方法,iOS有很多烦人的事情,但是UIView处理不是其中之一。:)
Martin Marconcini 2013年

127

接受的答案实际上并没有告诉您如何获取位置,因此这里有一些详细信息。您传入int长度为2 的数组,并且值将替换为视图的(x,y)坐标(在左上角)。

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

笔记

  • 在大多数情况下,替换getLocationOnScreengetLocationInWindow都可以得到相同的结果(请参阅此答案)。但是,如果您位于对话框或自定义键盘等较小的窗口中,则需要选择哪个更适合您的需求。
  • (0,0)如果调用此方法,则会得到成功,onCreate因为视图尚未布置。您可以使用a ViewTreeObserver侦听布局完成的时间,然后获取测量的坐标。(请参阅此答案。)

85

首先,您必须获取视图的localVisible矩形

例如:

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

希望这会有所帮助


3
这也应该起作用...但是它也给了我0值。如果可以帮助您弄清楚我做错了什么,我在上面包括了一个代码段。再次感谢。
史蒂夫·哈利

1
看到我的其他评论;在“意见”完成布置之前,我无意中称了这一点。现在工作正常,谢谢。
史蒂夫·海利

@Naveen穆尔蒂它给坐标特定视图,我需要点击的视图的坐标在根布局..
Aniket

19
这将返回相对于父级的位置。使用getGlobalVisibleRect()绝对COORDS。
杰弗里·布拉特曼

3
@SteveHaley,我一直遇到与您相同的问题,由于某种原因,它在我尝试的每种方法中均给我0。看来您已经弄清楚了为什么它给出零。我试图在加载布局后获取视图坐标,但我仍然为零,为什么呢?感谢您的帮助
潘卡Nimgade

46

使用全局布局侦听器一直对我有效。它的优点是,如果更改了布局(例如,如果将某些内容设置为View.GONE或添加/删除了子视图),则能够重新测量内容。

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

不要忘记在onGlobalLayout(){...}内部(末尾)删除OnGlobalLayoutListener。另外,请确保检查是否通过了!= 0; 侦听器可以被调用两次,一次是w = 0 / h = 0,第二次是实际的正确值。
MacD

这种膨胀主要布局然后使用setContentView()的方法Activity在我的应用程序中引起了问题!那是一个对话活动(windowFrame:@null, windowActionBar:false, windowIsFloating:true, windowNoTitle:true)。它的主要布局(a LinearLayout)没有确定的直系子代layout_width(全部为match_parentwrap_content)。
Mir-Ismaili

17

遵循罗曼·盖伊(Romain Guy)的评论后,这就是我如何解决它。希望它能对遇到此问题的其他人有所帮助。

实际上,我确实在尝试尝试将视图的位置放到屏幕上之前,但这种情况并不明显。这些行已在初始化代码运行后放置,因此我认为一切准备就绪。但是,此代码仍在onCreate()中;通过试验Thread.sleep(),我发现直到onCreate()一直执行到onResume()之后,布局才最终确定。所以的确,代码是在布局在屏幕上定位之前试图运行的。通过将代码添加到OnClickListener(或其他某种侦听器)中,可以获取正确的值,因为只有在布局完成后才能将其触发。


建议将以下行作为社区编辑:

请用 onWindowfocuschanged(boolean hasFocus)


所以您的意思是说setContentView(my_layout); 不会放置所有视图。所有视图仅在onCreate()完成后才能放置?
Ashwin 2012年

5
不完全的。所有视图均在之后存在setContentView(R.layout.something),但尚未以视觉方式加载。他们没有大小或职位。在第一次布局通过完成之前,这种情况不会发生,这会在将来的某个时候发生(通常在onResume()之后)。
史蒂夫·海利2012年

14

第一种方式:

Kotlin中,我们可以为视图创建一个简单的扩展:

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

并简单地获取坐标:

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

第二种方式:

第二种方法更简单:

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

并简单地得到X view.absX()和Yview.absY()


6
您的第一个样品的较短版本:val location = IntArray(2).apply(::getLocationOnScreen)
Tuan Chau

5

我的utils函数用于获取视图位置,它将返回Point带有x value和的对象y value

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

使用

Point viewALocation = getLocationOnScreen(viewA);

5

您可以使用getLocationOnScreen()或获取视图的坐标getLocationInWindow()

之后,x并且y应该在视图的左上角。如果您的根目录布局小于屏幕(例如在对话框中),则使用getLocationInWindow将相对于其容器而不是整个屏幕。

Java解决方案

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

注意:如果值始终为0,则可能会在请求位置之前立即更改视图。

为确保视图有机会更新,请在使用View计算新布局后运行您的位置请求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

注意:如果值始终为0,则可能会在请求位置之前立即更改视图。

为确保视图有机会更新,请在使用View计算新布局后运行您的位置请求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
}

如果需要可靠性,还可以添加:

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

0

使用此代码查找确切的X和Y坐标:

int[] array = new int[2];
ViewForWhichLocationIsToBeFound.getLocationOnScreen(array);
if (AppConstants.DEBUG)
    Log.d(AppConstants.TAG, "array  X = " + array[0] + ", Y = " + array[1]);
ViewWhichToBeMovedOnFoundLocation.setTranslationX(array[0] + 21);
ViewWhichToBeMovedOnFoundLocation.setTranslationY(array[1] - 165);

我添加/减去了一些值来调整我的观点。请仅在放大整个视图后再执行这些行。


“我添加/减去了一些值来调整我的看法。” 为什么??
制造商

我的意思是根据我的要求(视图的放置),我已经添加和减去了值来调整我的视图。
塔伦(Tarun)

0

在屏幕上同时获取视图位置和尺寸

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

            }
        });
    }

0

除了上述答案外,您还应在何时何地致电该问题getLocationOnScreen

在与您要获取的任何视图相关的任何信息中,只有将视图布置在屏幕上之后,该信息才可用。您可以通过在此(view.post(Runnable))内部放置依赖于视图创建的代码来轻松实现此目的:

view.post(new Runnable() {
            @Override
            public void run() {

               // This code will run view created and rendered on screen

               // So as the answer to this question, you can put
               int[] location = new int[2];
               myView.getLocationOnScreen(location);
               int x = location[0];
               int y = location[1];
            }
        });  
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.