支援不同的螢幕大小

如果應用程式支援各種螢幕大小,就能以最多種裝置和最多使用者存取您的應用程式。

如果想盡可能支援各種螢幕大小,就需要讓應用程式版面配置採用回應式及調適性設計。無論螢幕大小為何,回應式/調適性版面配置都可以提供最佳的使用者體驗,讓應用程式能配合手機、平板電腦、折疊式裝置、ChromeOS 裝置、直向和橫向,以及可調整大小的設定 (例如多視窗模式) 進行調整。

視窗大小類別

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

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

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

如圖所示,中斷點可讓您繼續思考裝置和設定的版面配置。每個大小類別中斷點都代表典型裝置情境的主要案例,在您考慮設計以中斷點為基礎的版面配置時,這個參考框架就非常實用。

大小類別 中斷點 裝置佔比
精簡寬度 寬度 < 600 dp 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「回應式版面配置格線」一文所述的版面配置中斷點。請使用視窗大小類別決定概略的應用程式版面配置,例如決定是否使用特定標準版面配置來善用額外的螢幕空間。

您可以使用 Jetpack WindowManager 程式庫提供的 WindowSizeClass#compute() 函式計算目前的 WindowSizeClass。以下範例說明如何計算視窗大小類別,並在視窗大小類別變更時接收更新:

Kotlin

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.
    }
}

Java

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 變更提供最多空間。接下來,再決定哪種版面配置適合中等寬度大小類別;建議您新增專門的版面配置。

後續步驟

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

如要進一步瞭解如何讓應用程式在所有裝置和螢幕大小上提供良好體驗,請參閱: