Android 如何获取视图的绝对坐标
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2224844/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get the absolute coordinates of a view
提问by Steve Haley
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?
我正在尝试获取视图左上角的绝对屏幕像素坐标。然而,所有的方法我可以找到诸如getLeft()
和getRight()
不工作,因为他们都似乎是相对于视图的父,因而给我0
。这样做的正确方法是什么?
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).
如果有帮助,这是一个“把图片放回原处”的游戏。我希望用户能够绘制一个框来选择多个部分。我的假设是,做到这一点的最简单方法是getRawX()
,并getRawY()
从MotionEvent
再与之比较的布局保持件的左上角的值。知道碎片的大小,我就可以确定选择了多少碎片。我意识到我可以在 上使用getX()
和,但是因为它返回一个相对位置,这使得确定选择了哪些部分变得更加困难。(并非不可能,我知道,但这似乎不必要地复杂)。getY()
MotionEvent
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 = (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.
编辑 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]);
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).
此代码是在所有初始化完成后添加的。图像已被划分为包含在TableLayout
. Cells[0] 是左上角ImageView
,我选择了 Cells[ 4],因为它在中间的某个地方,绝对不应该有 (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.)
上面显示的代码仍然给我日志中的所有0,我真的不明白,因为正确显示了各种拼图。(我为 tableLayoutCorners 和默认可见性尝试了 public int,两者都给出了相同的结果。)
I don't know if this is significant, but the ImageView
s are originally not given a size. The size of the ImageView
s 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.
我不知道这是否重要,但是ImageView
s 最初没有给出大小。ImageView
当我给它一个要显示的图像时,s的大小是在初始化期间由视图自动确定的。这是否会导致它们的值为 0,即使记录代码是在它们被赋予图像并自动调整大小之后?为了潜在地解决这个问题,我添加了tableLayout.requestLayout()
如上所示的代码,但这没有帮助。
回答by Romain Guy
回答by Suragch
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
长度为 2的数组,这些值将替换为视图的 (x, y) 坐标(左上角)。
int[] location = new int[2];
myView.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
Notes
笔记
- Replacing
getLocationOnScreen
withgetLocationInWindow
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 inonCreate
because the view has not been laid out yet. You can use aViewTreeObserver
to listen for when the layout is done and you can get the measured coordinates. (See this answer.)
- 在大多数情况下,替换
getLocationOnScreen
withgetLocationInWindow
应该会产生相同的结果(请参阅此答案)。但是,如果您在一个较小的窗口中,例如 Dialog 或自定义键盘,那么使用时您需要选择哪个更适合您的需要。 (0,0)
如果你调用这个方法,你会得到,onCreate
因为视图还没有布局。您可以使用 aViewTreeObserver
来监听布局何时完成,您可以获得测量的坐标。(请参阅此答案。)
回答by Naveen Murthy
First you have to get the localVisible rectangle of the view
首先,您必须获得视图的 localVisible 矩形
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
希望这会有所帮助
回答by Simon
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.
使用全局布局侦听器对我来说一直很有效。它的优点是可以在布局更改时重新测量事物,例如,如果某些内容设置为 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);
}
回答by Steve Haley
Following Romain Guy's comment, here's how I fixed it. Hopefully it'll help anyone else who also had this problem.
按照 Romain Guy 的评论,这是我修复它的方法。希望它可以帮助其他也遇到此问题的人。
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.
我确实试图在它们被布置在屏幕上之前获得视图的位置,但是这并不明显。这些行是在初始化代码运行后放置的,所以我认为一切都准备好了。但是,这段代码仍然在 onCreate(); 中。通过对 Thread.sleep() 进行试验,我发现在 onCreate() 一直到 onResume() 完成执行之后,布局实际上并未最终确定。所以确实,代码试图在布局完成定位在屏幕上之前运行。通过将代码添加到 OnClickListener(或其他一些侦听器)中,可以获得正确的值,因为它只能在布局完成后触发。
The line below was suggested as a community edit:
以下行被建议作为社区编辑:
please use onWindowfocuschanged(boolean hasFocus)
请用 onWindowfocuschanged(boolean hasFocus)
回答by Amir Hossein Ghasemi
First Way:
第一种方式:
In Kotlinwe can create a simple extension for view:
在Kotlin 中,我们可以为视图创建一个简单的扩展:
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()
并简单地得到绝对 X byview.absX()
和 Y byview.absY()
回答by Gibolt
You can get a View's coordinates using getLocationOnScreen()
or getLocationInWindow()
您可以使用getLocationOnScreen()
或获取视图的坐标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.
之后,x
并且y
应该是视图的左上角。如果您的根布局小于屏幕(如在对话框中),则使用getLocationInWindow
将相对于其容器,而不是整个屏幕。
Java Solution
Java解决方案
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.
注意:如果值始终为 0,则您可能会在请求位置之前立即更改视图。
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
以下命令计算视图的新布局后运行您的位置请求:
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.
注意:如果值始终为 0,则您可能会在请求位置之前立即更改视图。
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
以下命令计算视图的新布局后运行您的位置请求:
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)
}
}
回答by Phan Van Linh
My utils function for get view location, it will return a Point
object with x value
and y value
我的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]);
}
Using
使用
Point viewALocation = getLocationOnScreen(viewA);
回答by Hitesh Sahu
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]
}
}
});
}
回答by Suraj Vaishnav
Just in addition to the above answers, for the question where and when you should call getLocationOnScreen
?
除了上述答案之外,对于您应该在何时何地致电的问题getLocationOnScreen
?
In any information related to any view you want to get, it will be only available after the view has been laid out on the screen. You can easily do this by puttting your code which is dependent on view creation inside this(view.post(Runnable)):
在与您想要获取的任何视图相关的任何信息中,只有在将视图布置在屏幕上之后才能使用它。您可以通过将依赖于视图创建的代码放在 this(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];
}
});