折りたたみ型デバイス用の Jetpack Window Manager

Jetpack Window Manager には、すべての折りたたみ型デバイスを操作するための標準 API が用意されています。 これには、次の 2 つの重要なクラスが含まれています。

  • DisplayFeature - ヒンジや折りたたみなど、連続したフラット スクリーン表面の中断を識別します。 ウィンドウ マネージャーにより、レイアウト変更コールバックから表示機能のコレクションが返されます。
  • FoldingFeature - デバイスの特定の機能に関する情報を提供します。 Surface Duo には 1 つの折りたたみ機能しかありませんが、他のデバイスには複数ある場合があります。

同様のガイドが、Android Foldable Codelab にもあります。 折りたたみ型の開発の詳細については、Android のドキュメントを参照してください。Android チームによる例については、GitHub も参照してください。 Jetpack リリース ノートには、更新されると Window Manager の変更が記録されます。

ヒント

Surface Duo デュアルスクリーン ライブラリのコントロールとヘルパー クラスは Window Manager で動作します。 指示に従って、アプリ プロジェクトに適切なパッケージを追加します。

コードで Window Manager を直接使用するには、次の手順を実行します。

依存関係を追加する

  1. 最上位の build.gradle ファイルに mavenCentral() リポジトリがあることを確認します。

    allprojects {
        repositories {
            google()
            mavenCentral()
         }
    }
    
  2. モジュールレベル build.gradle ファイルで、compileSdkVersion および targetSdkVersion が API 31 以降に設定されていることを確認します。

    android { 
        compileSdkVersion 31
    
        defaultConfig { 
            targetSdkVersion 31
        } 
        ... 
    }
    
    
  3. モジュールレベル build.gradle ファイルに次の依存関係を追加します。

    dependencies {
        implementation "androidx.window:window:1.0.0"
        implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
    }
    

Kotlin コードで Window Manager を使用する

Kotlin プロジェクトで Window Manager プロパティにアクセスする場合は、正しい情報フローを設定することが重要です。 そうしないと、受け取るイベント更新プログラムが少なすぎるか、多すぎる場合があり、アプリのパフォーマンスに影響が生じる場合があります。

WindowInfoTracker オブジェクトを初期化して使用するには、次の手順に従います。

  1. MainActivity クラスで、WindowInfoTracker 用の変数を作成します。 import androidx.window.layout.WindowInfoTracker がファイルの先頭に追加されていることを確認します。

    class MainActivity : AppCompatActivity() {
        private lateinit var windowInfoTracker: WindowInfoTracker
    
  2. アクティビティの onCreate メソッドの WindowInfoTracker を初期化し、windowLayoutInfo プロパティから情報を収集するようにフローを設定します。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Initialize the window manager
        windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity)
    
        // Set up a flow
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoTracker.windowLayoutInfo(this@MainActivity)
                    .collect { 
                        // Check FoldingFeature properties here
                    }
            }
        }
    }
    

    次のインポートもファイルの先頭に追加されていることを確認します。

    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.lifecycleScope
    import androidx.lifecycle.repeatOnLifecycle
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.flow.collect
    import kotlinx.coroutines.launch
    
  3. 折りたたみ機能のプロパティWindowLayoutInfo フローを確認するコードを追加します。 このコードが実行されると、(折り目またはヒンジにまたがってスパンされている場合) アクティビティは現在のデバイスの状態と表示機能を使用して更新されます。

    次のコード スニペットでは、FoldingFeature のプロパティに基づいて異なるテキストがアクティビティにより表示されます。

    この例には、検出された折りたたみ機能のオクルージョン タイプと isSeparating 値を示す layout_change_text と呼ばれる TextView が含まれています。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity)
    
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoTracker.windowLayoutInfo(this@MainActivity)
                    .collect { newLayoutInfo ->
                        layout_change_text.text = "No display features detected"
                        for (displayFeature : DisplayFeature in newLayoutInfo.displayFeatures) {
                            if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.NONE) {
                                layout_change_text.text = "App is spanned across a fold, " +
                                    "isSeparating = ${displayFeature.isSeparating}"
                            }
                            if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.FULL) {
                                layout_change_text.text = "App is spanned across a hinge, " +
                                    "isSeparating = ${displayFeature.isSeparating}"
                            }
                        }
                    }
            }
        }
    }
    

折りたたみ機能のプロパティ

WindowLayoutInfo クラスには DisplayFeature 項目のコレクションがあります。そのうちの 1 つ以上が FoldingFeature クラスのインスタンスである可能性があります。

折りたたみ機能には、次のプロパティがあります。

  • bounds - 折りたたみ機能の外接する四角形の座標
  • occlusionType - 折りたたみ機能でコンテンツが隠れる場合 (FULL または NONE)
  • orientation - 折りたたみ機能の向き (HORIZONTAL または VERTICAL)
  • state - 折りたたみ機能の角度 (HALF_OPENED または FLAT)
  • isSeparating - 折りたたみ機能によって表示領域が 2 つの異なるセクションに分かれる場合

これらのプロパティに対してクエリを実行して、構成の変更後にレイアウトを調整する方法を決定できます。

isSeparating

コントロールを配置する場所または表示するコンテンツのペインの数を決定する場合は、isSeparating プロパティを使用します。 このフィールドにより、すべての折りたたみ型デバイス上で最適なユーザー エクスペリエンスがアプリで実現されます。

  • デュアルスクリーン デバイスの場合、アプリがヒンジにまたがる場合、これは常に当てはまります
  • その他の折りたたみ型デバイスの場合、これは、デバイスが卓上ポスチャにあるなど、状態が HALF_OPENED の時にのみ当てはまります

isSeparating プロパティを使用して、折りたたみ型デバイス用にアプリの UI レイアウトを調整するか、分離がない場合は既定の UI を使用するかを決定します。

private fun updateCurrentLayout(newLayoutInfo: WindowLayoutInfo) {
   for (displayFeature in newLayoutInfo.displayFeatures) {
       val foldFeature = displayFeature as? FoldingFeature
       foldFeature?.let {
           if (it.isSeparating) {
               // The content is separated by the FoldingFeature.
               // Here is where you should adapt your UI.
           } else {
               // The content is not separated.
               // Users can see and interact with your UI properly.
           }
       }
   }
}

このフィールドの使用方法の詳細な例については、こちらの isSeparating サンプルを参照してください。

Google でも、このプロパティに関連するドキュメントサンプルが、大画面および折りたたみ型のガイダンスの一部として提供されています。

サンプル

surface-duo-jetpack-window-manager-samples GitHub リポジトリには、Jetpack Window Manager や従来のビュー システムを使用してビルドされたさまざまなデュアルスクリーン ユーザー エクスペリエンスのパターンを示すいくつかの Kotlin サンプルが含まれています。

また、surface-duo-compose-samples GitHub リポジトリにも、Jetpack Window Manager を使用するデュアルスクリーン Kotlin サンプルが含まれていますが、これらのサンプルでは UI は Jetpack Compose でビルドされています。

Java API

Jetpack Window Manager と Java に関するブログ投稿とこの Java サンプルを参照して、WindowInfoTrackerCallbackAdapter を介して WindowInfoTracker クラスにアクセスする方法を確認してください。

リソース