使用視窗大小類別

試試 Compose 的方式
Jetpack Compose 是 Android 推薦的 UI 工具包。瞭解如何在 Compose 中使用視窗大小類別。

視窗大小類別是一組自主的可視區域中斷點,有助於設計、開發及測試回應式/自動調整式版面配置。這些中斷點經過特別挑選,可讓版面配置保持簡單又兼具彈性,進一步根據獨特的情境需求來最佳化應用程式。

視窗大小類別會將應用程式的可用顯示區域分類為「精簡」、「中等」或「展開」。可用寬度和高度會另外分類,因此應用程式隨時都有兩種視窗大小類別:寬度類別和高度類別。一般來說,可用寬度比可用高度更重要,這是因為直向捲動操作比較常見,因此寬度的視窗大小類別可能會與應用程式 UI 設定更相關。

圖 1. 以寬度為基礎的視窗大小類別圖示。
圖 2. 以高度為基礎的視窗大小類別圖示。

如圖所示,中斷點可讓您繼續考量裝置與設定的版面配置。每個大小類別中斷點都代表了典型裝置情境的大部分案例,當您考慮設計以中斷點為基礎的版面配置時,這個參考框架便可以派上用場。

大小類別 中斷點 裝置佔比
精簡寬度 寬度 < 600dp 99.96% 直向模式的手機
中等寬度 600 dp ≤ 寬度 < 840 dp 93.73% 直向模式下的平板電腦、

直向模式下未摺疊的大型內螢幕

展開寬度 寬度 ≥ 840 dp 97.22% 橫向模式下的平板電腦、

橫向模式下未摺疊的大型內螢幕

精簡高度 高度 < 480 dp 99.78% 橫向模式的手機
中等高度 480 dp ≤ 高度 < 900 dp 96.56% 橫向模式的平板電腦、

97.59% 直向模式的手機

展開高度 高度 ≥ 900 dp 94.25% 直向模式的平板電腦

雖然以實體裝置呈現大小類別有時很有幫助,但視窗大小類別並非明確取決於裝置螢幕大小。視窗大小類別不適用於 isTablet 類型邏輯。視窗大小類別取決於應用程式可用的視窗大小,且不考量執行應用程式的裝置類型,這會造成兩個重要結果:

  • 實體裝置不能保證特定的視窗大小類別。由於許多因素影響,應用程式能使用的螢幕空間可能不同於裝置螢幕大小。在行動裝置上,分割畫面模式可讓兩個應用程式同時在螢幕上顯示。在 ChromeOS 中,Android 應用程式能以可任意調整大小的桌面型視窗顯示。摺疊式裝置會因處於摺疊或未摺疊狀態,具有兩種不同大小的螢幕。

  • 視窗大小類別可能在應用程式的生命週期發生變化。執行應用程式時,變更裝置螢幕方向、進行多工處理和裝置處於摺疊/未摺疊狀態等因素,都會影響可用的螢幕空間大小。因此,視窗大小類別會動態變化,應用程式 UI 應隨之調整。

視窗大小類別可對應至 Material Design 版面配置指南中所述的精簡、中等和展開中斷點。請使用視窗大小類別決定概略的應用程式版面配置,例如決定是否使用特定標準版面配置來善用額外的螢幕空間。

就可以計算 WindowSizeClass敬上 方法是使用 WindowSizeClass#compute()。 函式Jetpack WindowManager 程式庫。以下範例 會顯示如何計算視窗大小類別,以及如何在有 視窗大小類別變更:

KotlinJava
class MainActivity : Activity() {
   
override fun onCreate(savedInstanceState: Bundle?) {
       
super.onCreate(savedInstanceState)

       
// ...

       
// Replace with a known container that you can safely add a
       
// view to where the view won't affect the layout and the view
       
// won't be replaced.
       
val container: ViewGroup = binding.container

       
// Add a utility view to the container to hook into
       
// View.onConfigurationChanged(). This is required for all
       
// activities, even those that don't handle configuration
       
// changes. You can't use Activity.onConfigurationChanged(),
       
// since there are situations where that won't be called when
       
// the configuration changes. View.onConfigurationChanged() is
       
// called in those scenarios.
        container
.addView(object : View(this) {
           
override fun onConfigurationChanged(newConfig: Configuration?) {
               
super.onConfigurationChanged(newConfig)
                computeWindowSizeClasses
()
           
}
       
})

        computeWindowSizeClasses
()
   
}

   
private fun computeWindowSizeClasses() {
       
val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this)
       
val width = metrics.bounds.width()
       
val height = metrics.bounds.height()
       
val density = resources.displayMetrics.density
       
val windowSizeClass = WindowSizeClass.compute(width/density, height/density)
       
// COMPACT, MEDIUM, or EXPANDED
       
val widthWindowSizeClass = windowSizeClass.windowWidthSizeClass
       
// COMPACT, MEDIUM, or EXPANDED
       
val heightWindowSizeClass = windowSizeClass.windowHeightSizeClass

       
// Use widthWindowSizeClass and heightWindowSizeClass.
   
}
}
public class MainActivity extends Activity {
   
@Override
   
protected void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);

       
// ...

       
// Replace with a known container that you can safely add a
       
// view to where the view won't affect the layout and the view
       
// won't be replaced.
       
ViewGroup container = binding.container;

       
// Add a utility view to the container to hook into
       
// View.onConfigurationChanged(). This is required for all
       
// activities, even those that don't handle configuration
       
// changes. You can't use Activity.onConfigurationChanged(),
       
// since there are situations where that won't be called when
       
// the configuration changes. View.onConfigurationChanged() is
       
// called in those scenarios.
        container
.addView(new View(this) {
           
@Override
           
protected void onConfigurationChanged(Configuration newConfig) {
               
super.onConfigurationChanged(newConfig);
                computeWindowSizeClasses
();
           
}
       
});

        computeWindowSizeClasses
();
   
}

   
private void computeWindowSizeClasses() {
       
WindowMetrics metrics = WindowMetricsCalculator.getOrCreate()
               
.computeCurrentWindowMetrics(this);

       
int width = metrics.getBounds().width
       
int height = metrics.getBounds().height()
       
float density = getResources().getDisplayMetrics().density;
       
WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density)
       
// COMPACT, MEDIUM, or EXPANDED
       
WindowWidthSizeClass widthWindowSizeClass = windowSizeClass.getWindowWidthSizeClass()
       
// COMPACT, MEDIUM, or EXPANDED
       
WindowHeightSizeClass heightWindowSizeClass = windowSizeClass.getWindowHeightSizeClass()

       
// Use widthWindowSizeClass and heightWindowSizeClass.
   
}
}

測試視窗大小類別

變更版面配置時,請測試所有視窗大小的版面配置行為,尤其是精簡、中等及展開中斷點寬度。

如果現有版面配置適用於精簡螢幕,請先針對展開寬度大小類別將版面配置最佳化,因為這個大小類別會為其他內容及 UI 變更提供最多空間。接著,再決定中等寬度大小類別適用何種版面配置,並考慮加入專用版面配置。

後續步驟

如要進一步瞭解如何使用視窗大小類別建立回應式/自動調整式版面配置,請參閱以下資源:

如要進一步瞭解如何讓應用程式在所有裝置和螢幕大小上都能順利運作,請參閱: