如何获得一个视图的绝对坐标

我试图获得一个视图的左上角的绝对屏幕像素坐标。然而,我能找到的所有方法,如getLeft()getRight()都不起作用,因为它们似乎都相对于视图的父类,因此给了我0。正确的做法是什么?

如果有帮助的话,这是一款“把图片按顺序放回去”的游戏。我希望用户能够画一个框来选择多个块。我的假设是最简单的方法是从MotionEventgetRawX()getRawY(),然后将这些值与保存这些块的布局的左上角进行比较。知道了棋子的大小,我就可以确定选中了多少棋子。我意识到我可以在MotionEvent上使用getX()getY(),但由于返回的是一个相对位置,这使得确定哪些片段被选中更加困难。(我知道,这不是不可能的,但似乎没有必要这么复杂)。

编辑:这是我用来尝试得到持有容器的大小的代码,每一个问题。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]);

这段代码是在所有初始化完成后添加的。图像被划分为包含在TableLayout中的ImageViews数组(cells[]数组)。单元格[0]是左上角ImageView,而我选择了单元格[4],因为它在中间的某个地方,最肯定的是它的坐标不应该是(0,0)。

上面显示的代码仍然在日志中给我所有的0,我真的不明白,因为各种拼图块被正确显示。(我尝试了公共int table elayoutcorners和默认可见性,两者都给出相同的结果。)

我不知道这是否重要,但__abc0最初没有给定大小。ImageViews的大小是在初始化过程中由视图自动确定的,当我给它一个图像来显示。这是否会导致它们的值为0,即使日志代码是在给它们一个图像并自动调整自己的大小之后?为了潜在地解决这个问题,我添加了如上所示的代码tableLayout.requestLayout(),但这并没有帮助。

356028 次浏览

首先,你必须获得视图的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));

希望这能有所帮助

根据Romain Guy的评论,以下是我如何修复它。希望它能帮助其他有这个问题的人。

我确实试图在视图显示在屏幕上之前得到视图的位置,但这一点都不明显。这些行是在初始化代码运行后放置的,所以我认为一切都准备好了。然而,这段代码仍然在onCreate()中;通过试验Thread.sleep(),我发现布局实际上并没有最终确定,直到onCreate()一直到onResume()已经完成执行。所以,实际上,代码试图在布局完成在屏幕上的定位之前运行。通过将代码添加到OnClickListener(或其他侦听器)中,可以获得正确的值,因为它只能在布局完成后被触发。


下面这句话是建议社区编辑的:

请使用onWindowfocuschanged(boolean hasFocus)

使用全局布局监听器对我来说一直都很有效。它的优点是,如果布局发生改变,例如,如果某些东西被设置为视图,则可以重新测量。添加/删除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);
}

公认的答案实际上并没有告诉如何获得位置,所以这里有更多的细节。你传入一个长度为2的int数组,这些值被替换为视图(左上角)的(x, y)坐标。

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

笔记

  • getLocationInWindow替换getLocationOnScreen在大多数情况下应该得到相同的结果(参见这个答案)。但是,如果你是在一个较小的窗口,如对话框或自定义键盘,那么使用你将需要选择哪一个更适合你的需求。
  • 如果你在onCreate中调用这个方法,你会得到(0,0),因为视图还没有被布局。你可以使用ViewTreeObserver来监听布局何时完成,你可以得到测量的坐标。(参见这个答案)。

我的获取视图位置的utils函数,它将返回一个包含x valuey valuePoint对象

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

第一个方法:

芬兰湾的科特林中,我们可以为view创建一个简单的扩展:

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

通过view.absX()view.absY()得到绝对的X和Y

你可以使用getLocationOnScreen()getLocationInWindow()来获取视图的坐标

之后,xy应该是视图的左上角。如果你的根布局小于屏幕(如在对话框中),使用getLocationInWindow将是相对于它的容器,而不是整个屏幕。

Java解决方案

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

注意:如果value总是0,你可能会在请求位置之前立即改变视图。

为了确保视图有机会更新,在使用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];
});

~~

芬兰湾的科特林解决方案

val point = IntArray(2)
view.getLocationOnScreen(point) // or getLocationInWindow(point)
val (x, y) = point

注意:如果value总是0,你可能会在请求位置之前立即改变视图。

为了确保视图有机会更新,在使用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)
}
}

在屏幕上获得视图位置和尺寸

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


}
});
}

除了上述答案之外,对于应该在何处和何时调用getLocationOnScreen?

对于任何与view相关的信息,只有在视图被布局(创建)在屏幕上之后才可用。因此,要获得位置,将代码放在view.post(Runnable)中,在view被布局后调用,如下所示:

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];
myView.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
}
});
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
}


})
这是当你触摸屏幕时获得屏幕视图坐标的Kotlin方法。 确保你同时处理UP和DOWN动作,否则可能会导致多次点击/触摸