获取视图相对于根布局的坐标

155

在Android中,我可以获取视图相对于Activity根布局的x和y位置吗?

11个回答

219

Android API 已经提供了一种实现的方法。 尝试这样做:

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;

这里是文档


22
这应该是被接受的答案。没有堆栈溢出的可能性,也没有数学错误的可能性。谢谢! - GLee
我想找到工具栏中视图的坐标。这是唯一一个能够始终考虑到android:fitsSystemWindows和从19到27各种API版本的方法。所以谢谢!这就是正确的答案。 - David Ferrand
运行完美无缺。 - Rawa
3
表现非常好,只想提一下 parentViewGroup 可以是视图层次结构中的任何一个父级,因此对于 ScrollView 也可以完美使用。 - Sira Lam
1
这行不通:在我的设备上,offsetTop始终为零。我使用了view.getParent()获取parentViewGroup。 - neoexpert
显示剩余7条评论

149

这是一种解决方案,但由于API随着时间的推移可能会发生变化,也可能有其他方法来完成它,请确保查看其他答案。其中一个声称更快,另一个声称更容易。

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

如果可以的话,请告诉我这是否可行。

这应该递归地添加每个父容器的顶部和左侧位置。 如果你愿意,你也可以用一个Point来实现它。


1
Log.i("RelativeLeft", ""+getRelativeLeft(findViewById(R.id.tv))); Log.i("RelativeTop", ""+getRelativeTop(findViewById(R.id.tv))); 我得到的都是0; 布局中的textView如下: <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" /> - pengwang
有趣的是,这对我返回了与getLocationOnScreen()相同的结果,后者也返回了与getLocationInWindow()相同的结果。尽管如此,您可以在参数中指定父级以结束递归,即获取相对于父级的xy坐标。 - Clocker
3
Android API已经提供了相对于根布局的坐标系。请查看此处:https://dev59.com/uHA65IYBdhLWcg3wzyH8#36740277。 - carlo.marinangeli
1
每次我从两个方法中得到了返回值0。我在RelativeLayout里面有一个TextView。 - Rohit Bandil
我已经找到了解决方案,调用onWindowFocusChanged(boolean hasFocus) Activity类方法中的两个方法。我认为只有当用户可以看到您的Activity时,这两种方法才能起作用。 - Rohit Bandil
显示剩余9条评论

98

请使用view.getLocationOnScreen(int[] location);(请参见Javadocs)。答案在整数数组中(x = location[0],y = location[1])。


14
这将返回相对于屏幕而不是活动的根布局的位置。 - fhucho
11
还有一个方法叫做View.getLocationInWindow(int[]),它返回视图相对于窗口的位置。 - Rubicon
6
要获取相对于根布局的位置,只需调用根布局和元素的 getLocationInWindow 方法,并使用减法运算。这比已接受的答案要简单得多,而且很可能更快。 - Blake Miller
我认为这是一个非常有用的答案,尽管也许可以编辑一下,以便为这个问题的观众提供一个简单的获取他们所寻找答案的方法。 - ramblinjan
1
这只是与屏幕相关的位置,而不是根。若要了解相对于根的位置,请阅读此处:https://dev59.com/uHA65IYBdhLWcg3wzyH8#36740277 - carlo.marinangeli
显示剩余5条评论

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

首先获取根布局,然后计算视图的坐标差异。
您还可以使用getLocationOnScreen()代替getLocationInWindow()


5
视图被缩放和旋转意味着什么? - DKV
这是唯一对我有效的答案。这应该被接受为答案。 - neoexpert
@DKV 你解决了关于缩放和旋转的问题吗?我也在寻找解决方案。 - Sumit Shukla
@DKV 你解决了关于缩放和旋转的问题吗?我也在寻找解决方案。 - undefined

38

不需要手动计算。

只需像这样使用getGlobalVisibleRect

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

注意对于中心坐标,不要使用类似以下的形式:

...
float two = (float) 2
float cx = myViewRect.left + myView.getWidth() / two;
float cy = myViewRect.top + myView.getHeight() / two;
你可以直接这样做:
float cx = myViewRect.exactCenterX();
float cy = myViewRect.exactCenterY();

查看Android源代码,getGlobalVisibleRect方法只是执行了globalOffset.set(-mScrollX, -mScrollY)操作,因此如果您只需要相对坐标,则可以使用getScollX/Y方法获取这些值。 - Xander
2
这应该是被接受的答案。 getLocationOnScreen 和 getGlobalVisibleRect 都可以使用。但是 getGlobalVisibleRect 提供了更多的信息,而且不需要使用原始数组。因此,我会选择 getGlobalVisibleRect。使用方法很简单。Rect positionRect = new Rect(); view.getGlobablVisibleRect(positionRect); //x = positionRect.left //y = positionRect.top. 祝好运! - JacksOnF1re
如果孩子的某些部分在此情况下不被考虑,该如何解决? - DKV
非常好的回答!非常感谢! - Rohit Kumar
有人能回答这个问题吗?@JacksOnF1re https://stackoverflow.com/questions/68868778/animate-progreessbar-when-it-reaches-to-the-specific-pointfrom-bottom-and-compl - Irfan Yaqub

8
你可以使用`view.getLocationOnScreen(int[] location)`来正确获取视图的位置。但是,如果在布局未被填充之前使用它,将会得到错误的位置。
解决这个问题的方法是添加ViewTreeObserver,像这样:全局声明一个数组来存储视图的x和y位置。
 int[] img_coordinates = new int[2];

接着在你的父布局上加入ViewTreeObserver,以获取布局膨胀的回调,然后再获取视图位置 否则你将得到错误的x和y坐标

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

然后像这样使用它

xposition = img_coordinates[0];
yposition =  img_coordinates[1];

4
我写了两个实用方法,可以在大多数情况下处理滚动、平移和缩放,但不能处理旋转。在尝试使用框架中的offsetDescendantRectToMyCoords()方法时,我发现它的准确性不一致。它在某些情况下有效,但在其他情况下会给出错误的结果。
"point"是一个包含两个元素(x和y坐标)的浮点数组,"ancestor"是树层次结构中位于"descendant"上方的viewgroup。
首先是一个从子视图坐标转换到祖先视图坐标的方法:
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);
    }
}

接下来是从祖先到后代的反向操作:
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;
}

2
如果有人还在尝试弄清楚这个问题,这就是如何获取view的中心X和Y坐标。
    int pos[] = new int[2];
    view.getLocationOnScreen(pos);
    int centerX = pos[0] + view.getMeasuredWidth() / 2;
    int centerY = pos[1] + view.getMeasuredHeight() / 2;

这在我的情况下不起作用...如果您能解决它,请查看问题。https://stackoverflow.com/questions/68868778/animate-progreessbar-when-it-reaches-to-the-specific-pointfrom-bottom-and-compl - Irfan Yaqub

1

我刚刚在这里找到答案

它说: 可以通过调用getLeft()和getTop()方法来检索视图的位置。前者返回表示视图的矩形的左侧或X坐标。后者返回表示视图的矩形的顶部或Y坐标。这些方法都返回相对于其父级的视图位置。例如,当getLeft()返回20时,这意味着视图位于其直接父级的左边缘右侧20像素处。

所以使用:

view.getLeft(); // to get the location of X from left to right
view.getRight()+; // to get the location of Y from right to left

1
这个位置是相对于视图的直接父级而言的,而不是根视图,因为根视图可能是父级的父级等等... - Rupert Rawnsley
视图被缩放和旋转是什么意思? - DKV

1
您可以使用以下代码来获取父视图和您感兴趣的视图之间的差异:
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];
}

不要忘记在视图绘制之后调用它:
 timeIndicator.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
        final int relativeTop = getRelativeTop(timeIndicator);
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接