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

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

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

図 1. スマートフォンを外部ディスプレイに接続し、スマートフォンが独自の状態を維持しながら、ディスプレイでデスクトップ セッションを実行している様子。

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

図 2. タブレットを外部モニターに接続し、デスクトップ セッションを両方のディスプレイに拡張している様子。

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

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

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

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

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

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

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

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

Jetpack Compose では、ディスプレイ固有の情報にアクセスできます。 CompositionLocal オブジェクトを使用します。たとえば、LocalConfiguration.currentLocalDensity.current などです。アクティビティまたはウィンドウがディスプレイ間を移動すると、デバイス構成が変更され、新しいディスプレイ指標で再コンポーズがトリガーされます。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 またはウィンドウ サイズクラスを使用して、さまざまな画面サイズと密度に対応したレスポンシブでアダプティブなレイアウトを作成します。

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

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

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

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

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

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