注意:原文章標題為「Java 反射靜態調用」,但內容實為 Android 獲取螢幕/視圖尺寸。本文依實際內容重寫並補充現代 API (WindowMetrics、ViewBinding) 與最佳實踐。
在 Android 開發中,「獲取寬高」看似簡單,實則暗藏 生命週期時機、API 版本碎片化、多窗口/折疊屏適配、單位換算 等陷阱。本文系統梳理螢幕尺寸、視圖尺寸、DP/PX 轉換的完整解決方案。
// Activity/Context 內 WindowMetrics metrics = getWindowManager().getCurrentWindowMetrics(); Rect bounds = metrics.getBounds(); // 包含狀態欄、導航欄、裝飾區 int width = bounds.width(); int height = bounds.height(); // 獲取可用區域 (扣除系統欄) WindowInsets insets = metrics.getWindowInsets(); Insets systemInsets = insets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()); int usableWidth = width - systemInsets.left - systemInsets.right; int usableHeight = height - systemInsets.top - systemInsets.bottom;
// 1. DisplayMetrics (最常用) DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int widthPx = dm.widthPixels; int heightPx = dm.heightPixels; float density = dm.density; // dp = px / density // 2. 獲取真實物理尺寸 (含狀態欄/導航欄) - API 17+ DisplayMetrics realDm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getRealMetrics(realDm); int realWidth = realDm.widthPixels; int realHeight = realDm.heightPixels;
onConfigurationChanged (Activity) 或 WindowSizeClassifier (Jetpack WindowManager)。WindowMetricsCalculator.getOrCreate(context).computeCurrentWindowMetrics(activity) 統一跨版本 API。| 時機 | 方法 | 可靠性 | 適用場景 |
|---|---|---|---|
| onCreate/onStart/onResume | view.getWidth() | ❌ 總是 0 | 不可用 |
| onWindowFocusChanged(true) | view.getWidth() | ✅ 可靠 | 傳統方案,但可能多次觸發 |
| ViewTreeObserver.OnGlobalLayoutListener | view.getWidth() | ✅ 可靠 | **推薦通用方案** |
| View.onSizeChanged(w,h,oldw,oldh) | 參數 w,h | ✅ 最準確 | **自定義 View 內部首選** |
| View.doOnNextLayout / doOnLayout (KTX) | lambda 中獲取 | ✅ 簡潔 | **Kotlin 專案首選** |
| 陷阱 | 錯誤做法 | 正確做法 |
|---|---|---|
| onCreate 獲取 View 寬高 | view.getWidth() | ViewTreeObserver / onSizeChanged / doOnNextLayout |
| 硬編碼螢幕尺寸 | 1080x1920 | 動態獲取 + 多窗口/折疊屏監聽 |
| 忽略狀態欄/導航欄 | getMetrics(dm) 直接用 | WindowInsets / getRealMetrics / WindowMetrics 區分 |
| DP/PX 手動乘除 density | px = (int)(dp * density) | TypedValue.applyDimension (處理縮放、四捨五入) |
| 跨進程/系統服務獲取 | WindowManagerService 內部 API | 公開 API: WindowManager / DisplayMetrics / WindowMetrics |
| 假設寬高固定 | 靜態常量 | ConfigurationChanged / WindowSizeClass 適配 |
dependencies {
implementation "androidx.window:window:1.2.0"
implementation "androidx.window:window-java:1.2.0" // Java 專用 API
}
// 獲取當前窗口指標
WindowMetricsCalculator calculator = WindowMetricsCalculator.getOrCreate(context);
WindowMetrics metrics = calculator.computeCurrentWindowMetrics(activity);
Rect bounds = metrics.getBounds();
// 獲取折疊屏狀態
WindowLayoutInfo layoutInfo = calculator.computeMaximumWindowMetrics(activity);
// 監聽變更
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(context)
.windowLayoutInfo(activity)
.collect { info -> /* 適配折疊/鉸鏈 */ }
}
}
WindowMetrics,舊版用 DisplayMetrics,區分「含系統欄」與「可用區域」。onSizeChanged,外部觀察用 ViewTreeObserver 或 KTX doOnNextLayout。TypedValue.applyDimension。androidx.window 庫,監聽 WindowLayoutInfo 動態適配。尺寸獲取的本質是「在正確的生命週期時機,用正確的 API,處理正確的邊界情況」。建立團隊統一的尺寸工具類,封裝版本兼容與生命週期邏輯,是杜絕「寬高為 0」Bug 的根本之道。