使用 WindowManager (最常用、最推荐)
这是在 Activity 或其他拥有 Window Context 的场景下最直接、最推荐的方法,它获取的是当前应用窗口的实际像素尺寸。

(图片来源网络,侵删)
获取屏幕的宽度和高度 (像素)
// 在 Activity 中调用
fun getScreenSize() {
// 1. 获取 WindowManager
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
// 2. 获取默认显示对象
val display = windowManager.defaultDisplay
// 3. 创建 DisplayMetrics 对象
val outMetrics = DisplayMetrics()
// 4. 将显示参数获取到 outMetrics 中
display.getMetrics(outMetrics)
// 5. 从 outMetrics 中取出宽高
val screenWidthPixels = outMetrics.widthPixels
val screenHeightPixels = outMetrics.heightPixels
Log.d("ScreenParams", "屏幕宽度 (像素): $screenWidthPixels")
Log.d("ScreenParams", "屏幕高度 (像素): $screenHeightPixels")
}
获取屏幕密度 (DPI) 和缩放比例
// 接续上面的代码
val density = outMetrics.density // 屏幕密度 (逻辑密度),2.0 表示 mdpi
val densityDpi = outMetrics.densityDpi // 屏幕密度 DPI,240 表示 hdpi
val scaledDensity = outMetrics.scaledDensity // 字体的缩放密度,通常和 density 相同
val xdpi = outMetrics.xdpi // 屏幕的物理 X DPI
val ydpi = outMetrics.ydpi // 屏幕的物理 Y DPI
Log.d("ScreenParams", "屏幕密度 (density): $density")
Log.d("ScreenParams", "屏幕密度 DPI (densityDpi): $densityDpi")
Log.d("ScreenParams", "字体缩放密度 (scaledDensity): $scaledDensity")
Log.d("ScreenParams", "物理 X DPI: $xdpi")
Log.d("ScreenParams", "物理 Y DPI: $ydpi")
计算屏幕的物理尺寸 (英寸)
利用勾股定理计算屏幕对角线的物理长度。
// 接续上面的代码
val screenWidthInches = outMetrics.widthPixels / xdpi
val screenHeightInches = outMetrics.heightPixels / ydpi
val screenDiagonalInches = sqrt(screenWidthInches * screenWidthInches + screenHeightInches * screenHeightInches)
Log.d("ScreenParams", "屏幕物理宽度 (英寸): $screenWidthInches")
Log.d("ScreenParams", "屏幕物理高度 (英寸): $screenHeightInches")
Log.d("ScreenParams", "屏幕对角线尺寸 (英寸): $screenDiagonalInches") // 这就是我们常说的手机尺寸,如 6.1英寸
使用 Resources 和 DisplayMetrics
这种方法也非常方便,尤其在需要处理资源时。Resources 对象本身就持有一个 DisplayMetrics 实例。
fun getScreenSizeWithResources() {
// Resources 对象会自动管理 DisplayMetrics
val resources = resources
val displayMetrics = resources.displayMetrics
val screenWidthPixels = displayMetrics.widthPixels
val screenHeightPixels = displayMetrics.heightPixels
val densityDpi = displayMetrics.densityDpi
Log.d("ScreenParams_Resources", "屏幕宽度 (像素): $screenWidthPixels")
Log.d("ScreenParams_Resources", "屏幕高度 (像素): $screenHeightPixels")
Log.d("ScreenParams_Resources", "屏幕密度 DPI: $densityDpi")
}
注意: Resources 的 DisplayMetrics 在横竖屏切换或系统 DPI 设置改变时会自动更新,但不会实时反映当前窗口的状态(分屏模式下,它返回的是整个屏幕的尺寸,而不是当前分屏窗口的尺寸),对于精确获取窗口尺寸,WindowManager 仍然是首选。
使用 WindowInsets (获取安全区域)
现代 Android 应用需要考虑状态栏、导航栏等系统UI元素。WindowInsets 可以帮助你获取这些信息,从而计算出真正的“可用”屏幕区域。

(图片来源网络,侵删)
这对于实现沉浸式体验或全屏布局非常有用。
// 在 Activity 的 onCreate 或 onWindowFocusChanged 中调用
fun getUsableScreenSize() {
// 使用 ViewCompat 获取 WindowInsets,兼容性更好
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, insets ->
// 获取状态栏高度
val statusBarHeight = insets.getInsets(WindowInsets.Type.statusBars()).top
// 获取导航栏高度 (可能在底部或侧边)
val navigationBarHeight = insets.getInsets(WindowInsets.Type.navigationBars()).bottom
// 可用屏幕尺寸 = 屏幕总尺寸 - 系统UI区域
val usableWidth = insets.getInsets(WindowInsets.Type.systemBars()).left + insets.getInsets(WindowInsets.Type.systemBars()).right
val usableHeight = insets.getInsets(WindowInsets.Type.systemBars()).top + insets.getInsets(WindowInsets.Type.systemBars()).bottom
// 注意:这里为了简化,直接用屏幕总尺寸减去 insets
// 更精确的计算方式如下:
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
val totalWidth = displayMetrics.widthPixels
val totalHeight = displayMetrics.heightPixels
val actualUsableWidth = totalWidth - usableWidth
val actualUsableHeight = totalHeight - usableHeight
Log.d("WindowInsets", "状态栏高度: $statusBarHeight")
Log.d("WindowInsets", "导航栏高度: $navigationBarHeight")
Log.d("WindowInsets", "可用宽度: $actualUsableWidth")
Log.d("WindowInsets", "可用高度: $actualUsableHeight")
// 返回 insets 以便后续视图可以使用
insets
}
}
使用 ViewTreeObserver (监听尺寸变化)
如果你想在屏幕尺寸发生变化(如键盘弹出、横竖屏切换)时执行某些操作,可以使用 ViewTreeObserver。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rootLayout = findViewById<View>(android.R.id.content)
rootLayout.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
// 当布局完成或尺寸变化时调用
val width = rootLayout.width
val height = rootLayout.height
Log.d("ViewTreeObserver", "当前布局尺寸 - 宽: $width, 高: $height")
// 注意:为了避免重复调用,在不需要时移除监听器
// API 16+ 使用 isAlive 检查
if (rootLayout.viewTreeObserver.isAlive) {
rootLayout.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
}
})
}
总结与最佳实践
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
WindowManager |
通用,获取精确的窗口尺寸和屏幕密度 | 最直接、最准确,能反映当前窗口状态 | 需要处理 WINDOW_SERVICE |
Resources |
需要访问资源或了解设备配置时 | 非常方便,与资源系统紧密集成 | 不反映当前窗口状态(如分屏) |
WindowInsets |
处理安全区域,实现沉浸式布局 | 能精确获取系统UI区域,实现更灵活的UI | API 较新,需要处理兼容性 |
ViewTreeObserver |
监听视图尺寸的实时变化 | 能在尺寸变化时得到回调 | 需要手动管理监听器的添加和移除 |
完整示例代码 (Kotlin):
你可以创建一个 Activity,将下面的代码粘贴进去运行,就能看到所有输出。

(图片来源网络,侵删)
import android.content.Context
import android.os.Bundle
import android.util.DisplayMetrics
import android.util.Log
import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 1. 使用 WindowManager 获取屏幕参数
getScreenParamsByWindowManager()
// 2. 使用 Resources 获取屏幕参数
getScreenParamsByResources()
// 3. 使用 WindowInsets 获取安全区域
getSafeAreaByWindowInsets()
}
private fun getScreenParamsByWindowManager() {
Log.d("--- WindowManager 方法 ---", "开始获取...")
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
val display = windowManager.defaultDisplay
val outMetrics = DisplayMetrics()
display.getMetrics(outMetrics)
val widthPixels = outMetrics.widthPixels
val heightPixels = outMetrics.heightPixels
val density = outMetrics.density
val densityDpi = outMetrics.densityDpi
val xdpi = outMetrics.xdpi
val ydpi = outMetrics.ydpi
// 计算物理尺寸
val widthInches = widthPixels / xdpi
val heightInches = heightPixels / ydpi
val diagonalInches = kotlin.math.sqrt(widthInches * widthInches + heightInches * heightInches)
Log.d("WindowManager", "宽度 (像素): $widthPixels")
Log.d("WindowManager", "高度 (像素): $heightPixels")
Log.d("WindowManager", "密度: $density")
Log.d("WindowManager", "密度 DPI: $densityDpi")
Log.d("WindowManager", "物理尺寸 (英寸): %.2f".format(diagonalInches))
}
private fun getScreenParamsByResources() {
Log.d("--- Resources 方法 ---", "开始获取...")
val displayMetrics = resources.displayMetrics
Log.d("Resources", "宽度 (像素): ${displayMetrics.widthPixels}")
Log.d("Resources", "高度 (像素): ${displayMetrics.heightPixels}")
Log.d("Resources", "密度 DPI: ${displayMetrics.densityDpi}")
}
private fun getSafeAreaByWindowInsets() {
Log.d("--- WindowInsets 方法 ---", "开始获取...")
val rootLayout = findViewById<View>(android.R.id.content)
ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { _, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
val statusBarHeight = systemBars.top
val navigationBarHeight = systemBars.bottom
Log.d("WindowInsets", "状态栏高度: $statusBarHeight")
Log.d("WindowInsets", "导航栏高度: $navigationBarHeight")
insets // 必须返回 insets
}
}
}
重要提示:
widthPixels和heightPixels是指设备屏幕上可用的像素数量,包含了系统UI(如状态栏)。densityDpi是 Android 系统用来适配不同屏幕密度的关键值。mdpi(160),hdpi(240),xhdpi(320),xxhdpi(480) 等。- 横竖屏: 如果你的应用支持横竖屏,记得在
AndroidManifest.xml中为<activity>标签添加android:configChanges="orientation|screenSize",并在onConfigurationChanged中重新获取屏幕参数,以避免 Activity 重建带来的性能开销。
