彻底搞懂 Android View 坐标体系:从绝对坐标到相对视图坐标

在 Android 自定义 View 以及处理滑动冲突时,最容易让人头疼的就是各种各样的坐标计算。getX()getRawX()getLeft() 之间有什么区别?为什么滑着滑着数值就变了?

搞懂这些问题的前提是清晰划分 Android 中的两大坐标系:Android 屏幕坐标系 与 View 相对坐标系。本文将带你一次性理清它们的关系。


一、 Android 坐标系(绝对坐标)

Android 屏幕坐标系是以整个屏幕作为参照物的物理坐标系。

  • 原点 (0, 0):屏幕左上角的顶点。
  • X 轴正方向:原点水平向右。
  • Y 轴正方向:原点垂直向下。

Android 绝对坐标系

在此坐标系中,任何一个点的位置都是绝对的。通过 MotionEventgetRawX()getRawY() 获得的就是触摸点在 Android 坐标系中的绝对坐标


二、 View 坐标系(相对坐标)

在布局与绘图时,我们更关心的是 View 之间的相对位置。View 坐标系是以父容器的左上角作为原点的相对坐标系。

1. View 的四个顶点顶点坐标

一个 View 的物理大小和位置主要由它的四个顶点(Top, Left, Right, Bottom)决定:

  • Left (左):View 左边缘到其父布局左边缘的距离。
  • Top (上):View 上边缘到其父布局上边缘的距离。
  • Right (右):View 右边缘到其父布局左边缘的距离。
  • Bottom (下):View 下边缘到其父布局上边缘的距离。

View 相对坐标系

这四个值在 View 中对应了四个成员变量 mLeftmTopmRightmBottom。我们可以通过以下 Getter 方法直接获取这组相对坐标:

  • getLeft()
  • getTop()
  • getRight()
  • getBottom()

2. View 的宽度与高度

由相对坐标关系很容易推导出 View 的宽度(Width)与高度(Height)的计算公式:

在 Android SDK 的 View.java 源码中,对应实现也非常直接:

/**  
 * Return the width of your view. 
 * 
 * @return The width of your view, in pixels.  
 */
@ViewDebug.ExportedProperty(category = "layout")  
public final int getWidth() {  
    return mRight - mLeft;  
}  
  
/**  
 * Return the height of your view. 
 * 
 * @return The height of your view, in pixels.  
 */
@ViewDebug.ExportedProperty(category = "layout")  
public final int getHeight() {  
    return mBottom - mTop;  
}

NOTE

这里的 getWidth() / getHeight() 得到的是 View 最终呈现在屏幕上的布局宽高。这与 getMeasuredWidth() / getMeasuredHeight()(测量宽高)在概念上是不同的,虽然大多数情况下它们的值相等。


三、 触摸事件中的坐标:MotionEvent 坐标对比

在处理点击事件或手势滑动时,onTouchEvent(MotionEvent event) 中提供了两组获取触摸点坐标的方法。以屏幕中某个被触摸的 View 为例,两组坐标的含义如下:

方法坐标类型坐标系原点物理含义
getX()视图坐标(相对)当前被触摸 View 的左上角触摸点距离当前 View 左边缘的距离
getY()视图坐标(相对)当前被触摸 View 的左上角触摸点距离当前 View 上边缘的距离
getRawX()屏幕坐标(绝对)整个手机屏幕的左上角触摸点距离屏幕左边缘的距离
getRawY()屏幕坐标(绝对)整个手机屏幕的左上角触摸点距离屏幕顶边缘的距离

实战应用场景

  • getX() / getY():通常用于实现 View 内部的局部滑动、绘图追踪、或者判断手指是否仍在控件的边界内。
  • getRawX() / getRawY():通常用于实现全屏范围内的跟手拖动。因为在拖动 View 时,View 自身的位置会发生改变,使用 getX() / getY() 作为基准会导致坐标的计算源发生“抖动”,而 getRawX() / getRawY() 提供绝对的物理屏幕参考,能避免抖动问题。

四、 总结与延伸

掌握了这两套坐标系,你在面对诸如“计算 View 的滑动偏移量”或者“判断两个 View 是否重叠”等问题时,就能做到游刃有余。接下来,推荐阅读 实现 View 滑动的多种方式 了解如何基于坐标变化控制 View 的运动,或者阅读 View 的事件分发机制 了解点击事件是如何流向这些坐标区域的。