接続されたディスプレイをサポートする

接続されたディスプレイは、デスクトップ ウィンドウ エクスペリエンスを標準のスマートフォンに拡張し、ユーザーがモバイル デバイスから大画面にアクセスできるようにします。この機能により、アプリの操作性とユーザーの生産性に新たな可能性が広がります。

デスクトップ ウィンドウのすべての独自機能が、接続されたディスプレイに適用されます。スマートフォンをディスプレイに接続しても、スマートフォンの状態は変わらず、接続されたディスプレイで空白のデスクトップ セッションが開始されます。デバイスとディスプレイは 2 つの個別のシステムとして機能し、各ディスプレイに固有のアプリが割り当てられます。

図 1. 外部ディスプレイに接続されたスマートフォン。ディスプレイにデスクトップ セッションが表示され、スマートフォンは独自の状態を維持しています。

タブレットなど、デスクトップ ウィンドウが有効なデバイスを外部モニターに接続すると、デスクトップ セッションが両方のディスプレイに拡張されます。2 つのディスプレイが 1 つの連続したシステムとして機能します。この設定により、ウィンドウ、コンテンツ、カーソルを 2 つのディスプレイ間で自由に移動できます。

図 2. 外部モニターに接続されたタブレット。デスクトップ セッションが両方のディスプレイに拡張されています。

接続ディスプレイを効果的にサポートするには、アプリの設計と実装のいくつかの側面に注意を払う必要があります。以下のベスト プラクティスを実践することで、スムーズで生産性の高いユーザー エクスペリエンスを実現できます。

動的なディスプレイの変更を処理する

多くのアプリは、Display オブジェクトとその特性がアプリのライフサイクル中に変化しないことを前提として構築されています。ただし、ユーザーが外部モニターを接続または切断したり、アプリ ウィンドウをディスプレイ間で移動したりすると、アプリのコンテキストまたはウィンドウに関連付けられている基盤となる Display オブジェクトが変更される可能性があります。サイズ、解像度、リフレッシュ レート、HDR サポート、密度など、ディスプレイのプロパティはすべて異なる場合があります。たとえば、スマートフォンの画面に基づいて値をハードコードすると、外部ディスプレイでレイアウトが崩れる可能性があります。

外部ディスプレイのピクセル密度も大きく異なる場合があります。アプリが密度の変化に正しく応答することを確認する必要があります。これには、レイアウトに密度非依存ピクセル(dp)を使用すること、密度固有のリソースを提供すること、UI が適切にスケーリングされるようにすることが含まれます。

外部ディスプレイでアクティビティが実行されているときにディスプレイが切断されると、システムはアクティビティをメイン ディスプレイに移動します。この移動により、画面サイズや密度の変更などの構成変更がトリガーされ、アクティビティが再作成される可能性があります。アプリは、UI の状態を保存して復元することで構成変更を処理し、データの損失やユーザー エクスペリエンスの混乱を防ぐ必要があります。

適切なコンテキストを使用する

マルチディスプレイ環境では、適切なコンテキストを使用することが重要です。リソースにアクセスする場合、アクティビティ コンテキスト(表示される)はアプリケーション コンテキスト(表示されない)とは異なります。

アクティビティ コンテキストはディスプレイに関する情報を含んでおり、アクティビティの表示領域に合わせて常に調整されます。これにより、アプリの表示密度やウィンドウ指標に関する正しい情報を取得できます。現在のウィンドウまたはディスプレイに関する情報を取得するには、常にアクティビティ コンテキスト(または別の UI ベースのコンテキスト)を使用します。これは、コンテキストの情報を使用する一部のシステム API にも影響します。

Jetpack Compose では、LocalConfiguration.current や LocalDensity.current などの CompositionLocal オブジェクトを使用して、ディスプレイ固有の情報にアクセスできます。アクティビティまたはウィンドウがディスプレイ間を移動すると、デバイス設定が変更され、新しいディスプレイ指標による再コンポーズがトリガーされます。CompositionLocal オブジェクトを使用すると、UI をシームレスに適応させることができます。

ディスプレイ情報を取得する

Display クラスを使用すると、ディスプレイのサイズ、密度、フラグなどの情報を取得できます。DisplayManager システム サービスを使用して、利用可能なディスプレイを取得します。外部ディスプレイを識別するには、Display.DEFAULT_DISPLAY を除外します。これは通常、組み込みのスマートフォンまたはタブレットの画面です。

val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val displays = displayManager.getDisplays()
// The default display is 0. External displays have other IDs.
val externalDisplays = displays.filter { it.displayId != Display.DEFAULT_DISPLAY }

アクティビティの起動と構成を管理する

接続されたディスプレイを使用すると、アプリは起動時または別のアクティビティの作成時に、アプリを実行するディスプレイを指定できます。この動作は、マニフェスト ファイルで定義されているアクティビティ起動モードと、アクティビティを起動するエンティティによって設定されたインテント フラグとオプションによって異なります。

アクティビティがセカンダリ ディスプレイに移動すると、アプリでコンテキストの更新、ウィンドウのサイズ変更、構成とリソースの変更が発生する可能性があります。アクティビティが構成変更を処理する場合、onConfigurationChanged() で通知されます。それ以外の場合、アクティビティは再起動されます。

アクティビティに選択した起動モードで複数のインスタンスが許可されている場合、セカンダリ画面で起動すると、アクティビティの新しいインスタンスが作成されることがあります。両方のアクティビティが同時に再開されるため、特定のマルチタスク シナリオで役立ちます。

ActivityOptions を使用して、特定ディスプレイでアクティビティを起動できます。launchDisplayId には Android 8(API レベル 26)以降が必要です。

// Get DisplayManager and find the first external display.
val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val externalDisplayId = displayManager.displays
    .firstOrNull { it.displayId != Display.DEFAULT_DISPLAY }
    ?.displayId

// If an external display is found, launch the activity on it.
if (externalDisplayId != null) {
    val intent = Intent(this, MySecondaryActivity::class.java)
    val options = ActivityOptions.makeBasic()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        options.launchDisplayId = externalDisplayId
    }
    startActivity(intent, options.toBundle())
} else {
    // Optionally, handle the case where no external display is connected.
}

デバイスの許可リストを避ける

アプリによっては、許可リストを使用するか、BUILD.MODEL と組み込みディスプレイのサイズを確認することで、大画面の UI と機能を特定のデバイスに制限することがあります。このアプローチは、接続されたディスプレイには効果的ではありません。実質的にどのデバイスも大画面に接続でき、外部ディスプレイが接続されてもデバイスモデルは変わらないためです。

許可リストを使用したり、BUILD.MODEL や組み込みのディスプレイ サイズを確認したりする代わりに、実行時にウィンドウ指標やデバイスの機能を確認して UI を決定します。Jetpack WindowManager API またはウィンドウ サイズクラスを使用して、さまざまな画面サイズと密度に対応するレスポンシブ レイアウトとアダプティブ レイアウトを構築します。

外部周辺機器をサポートする

ユーザーは外部ディスプレイに接続すると、多くの場合、よりデスクトップに近い環境を作成します。これには、外付けキーボード、マウス、トラックパッド、ウェブカメラ、マイク、スピーカーの使用が含まれることがよくあります。アプリがこれらの周辺機器とシームレスに連携するようにする必要があります。これには、キーボード ショートカットの処理、マウスポインタの操作の管理、外部カメラやマイクの適切なサポート、オーディオ出力ルーティングの尊重が含まれます。詳しくは、大画面での入力の互換性をご覧ください。

ユーザーの生産性を高める

接続されたディスプレイは、ユーザーの生産性を向上させる大きな機会となります。これで、デスクトップ アプリケーションに匹敵するエクスペリエンスを提供できるモバイルアプリを構築するためのツールが揃いました。ユーザーの生産性を高めるために、次の機能の実装を検討してください。

  • ユーザーが同じアプリの複数のインスタンスを開けるようにします。これは、ドキュメントの比較、異なる会話の管理、複数のファイルの同時表示などのタスクに非常に役立ちます。
  • ドラッグ&ドロップにより、ユーザーがアプリの内外でリッチデータを共有できるようにします。
  • 堅牢な状態管理システムを実装することで、構成変更後もユーザーがワークフローを維持できるようにします。

これらのガイドラインに沿って、提供されているコード例を利用することで、接続されたディスプレイにシームレスに適応するアプリを作成し、ユーザーに、より豊かで生産性の高いエクスペリエンスを提供できます。