如何获取视图的绝对坐标

Ste*_*ley 376 android android-tablelayout

我正在尝试获取视图左上角的绝对屏幕像素坐标.但是,我可以找到的所有方法,例如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());
Run Code Online (Sandbox Code Playgroud)

编辑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]);
Run Code Online (Sandbox Code Playgroud)

所有初始化完成后添加此代码.图像被分成一个包含在a中的ImageViews(cells []数组)数组TableLayout.单元格[0]是左上角ImageView,我选择了单元格[4],因为它位于中间的某个位置,绝对不应该有(0,0)的坐标.

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

我不知道这是否重要,但ImageViews最初没有给出尺寸.ImageView当我给它一个要显示的图像时,视图自动初始化时确定s 的大小.这可能会导致它们的值为0,即使这些日志代码是在它们被赋予图像并自动调整大小之后吗?为了解决这个问题,我添加了tableLayout.requestLayout()如上所示的代码,但这没有帮助.

Rom*_*Guy 607

使用View.getLocationOnScreen()和/或getLocationInWindow().

  • 你只能调用它*AFTER*布局已经发生.您在视图位于屏幕上之前调用方法. (74认同)
  • 你的意思是ViewTreeObserver和它的OnGlobalLayoutListener?请参阅View.getViewTreeObserver()以开始使用. (9认同)
  • @RomainGuy是的,有点像这样,但比注册(然后取消注册......)更容易混淆.在SDK方面,iOS是一个比Android更好的领域.每天使用两者,我可以说iOS有很多讨厌的东西,但UIView处理不是其中之一.:) (8认同)
  • 啊哈,你是对的,虽然我这样做并不明显.谢谢!对于任何对细节感兴趣的人,我都添加了一个解释我的意思的答案. (5认同)
  • 现在,这是我期望找到的那种功能.不幸的是,我一定不能正确使用它.我意识到这是一个已知的错误,getLocationOnScreen在Android v1.5(我编码的平台)中给出了空指针异常,但是在v2.1中的测试没有任何效果.两种方法都给了我0秒的一切.我在上面添加了一个代码段. (3认同)

Sur*_*gch 107

接受的答案实际上没有说明如何获得位置,所以这里有更多细节.传入int长度为2 的数组,并将值替换为视图的(x,y)坐标(顶部,左上角).

int[] location = new int[2];
myView.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
Run Code Online (Sandbox Code Playgroud)

笔记

  • 更换getLocationOnScreengetLocationInWindow应该给在大多数情况下,相同的结果(见这个答案).但是,如果您在像Dialog或自定义键盘这样的较小窗口中,那么您需要选择哪一个更适合您的需求.
  • (0,0)如果您调用此方法,则会得到,onCreate因为视图尚未布局.您可以使用a ViewTreeObserver来监听布局完成后您可以获得测量的坐标.(见这个答案.)


Nav*_*thy 78

首先,您必须获取视图的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));
Run Code Online (Sandbox Code Playgroud)

希望这会有所帮助

  • 这将返回相对于父级的位置.使用`getGlobalVisibleRect()`表示绝对坐标. (19认同)
  • 这也应该有效...但是它也给了我0的值.我上面包含了一个代码片段,如果这可以帮助你弄清楚我做错了什么.再次感谢. (3认同)
  • @SteveHaley,我一直面临同样的问题,因为某些原因它在我尝试的每种方法中都给了我0.看来你已经弄明白为什么它给出零.我正在尝试在加载布局后获取视图坐标仍然为零为什么会这样?谢谢你的帮助 (3认同)

Sim*_*mon 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);
 }
Run Code Online (Sandbox Code Playgroud)


Ste*_*ley 18

按照Romain Guy的评论,这是我如何修复它.希望它能帮助那些也有这个问题的人.

我确实试图在屏幕布局之前获得观点的位置,但事实并非如此.这些行已经在初始化代码运行后放置,所以我假设一切准备就绪.但是,此代码仍在onCreate()中; 通过试验Thread.sleep()我发现布局实际上没有完成,直到onCreate()一直到onResume()完成执行.实际上,代码试图在布局完成定位在屏幕上之前运行.通过将代码添加到OnClickListener(或其他一些Listener),获得了正确的值,因为它只能在布局完成后触发.


以下行被建议为社区编辑:

请用 onWindowfocuschanged(boolean hasFocus)

  • 不完全的.所有视图在'setContentView(R.layout.something)`之后'存在',但它们没有在视觉上加载.它们没有大小或位置.直到第一个布局传递结束才会发生这种情况,这发生在未来的某个时刻(通常在onResume()之后). (5认同)

Ami*_*emi 12

第一种方式:

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

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

并简单地获取坐标:

val location = yourView.getLocationOnScreen()
val absX = location.x
val absY = location.y
Run Code Online (Sandbox Code Playgroud)

第二种方式:

第二种方法更简单:

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]
}
Run Code Online (Sandbox Code Playgroud)

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

  • 第一个样本的简短版本:`val location = IntArray(2).apply(:: getLocationOnScreen)` (5认同)

Gib*_*olt 9

您可以使用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];
Run Code Online (Sandbox Code Playgroud)

注意:如果值始终为 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];
});
Run Code Online (Sandbox Code Playgroud)

~~

科特林解决方案

val point = IntArray(2)
view.getLocationOnScreen(point) // or getLocationInWindow(point)
val (x, y) = point
Run Code Online (Sandbox Code Playgroud)

注意:如果值始终为 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
}
Run Code Online (Sandbox Code Playgroud)

我建议创建一个扩展函数来处理这个:

// To use, call:
val (x, y) = view.screenLocation

val View.screenLocation get(): IntArray {
    val point = IntArray(2)
    getLocationOnScreen(point)
    return point
}
Run Code Online (Sandbox Code Playgroud)

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

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)
    }
}
Run Code Online (Sandbox Code Playgroud)


Sur*_*nav 6

除了上述答案之外,对于您应该在何时何地致电的问题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];
            }
        });  
Run Code Online (Sandbox Code Playgroud)


Pha*_*inh 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]);
}
Run Code Online (Sandbox Code Playgroud)

运用

Point viewALocation = getLocationOnScreen(viewA);
Run Code Online (Sandbox Code Playgroud)