マルチ ウィンドウのサポート

マルチ ウィンドウ モードでは、複数のアプリで同じ画面を同時に使用できます。アプリを左右または上下に並べて表示することも(分割画面モード)、1 つのアプリを小さなウィンドウで他のアプリに重ねて表示することも(ピクチャー イン ピクチャー モード)、個別のアプリを移動可能でサイズ変更可能な別のウィンドウに表示することもできます(フリーフォーム モード)。

図 1. 分割画面モードで 2 つのアプリを並べて表示します。

ユーザー エクスペリエンスは、Android OS のバージョンとデバイスのタイプによって異なります。

  • Android 7.0(API レベル 24)では、小画面のデバイスでは分割画面モード、一部のデバイスではピクチャー イン ピクチャー モードが導入されています。

    分割画面モードでは、2 つのアプリが左右または上下に並べて表示されます。ユーザーは、2 つのアプリの間の分割線をドラッグすることで、一方を拡大(他方を縮小)できます。

    ピクチャー イン ピクチャー モードでは、動画を再生しながら他のアプリを操作できます(ピクチャー イン ピクチャーのサポートをご覧ください)。

    大画面デバイスのメーカーは、ユーザーが各アクティビティを自由にサイズ変更できるフリーフォーム モードを有効にできます。

    マルチ ウィンドウ モードをアプリがどう処理するかを設定するには、アクティビティの最小許容ディメンションを指定します。また、resizeableActivity="false" に設定してアプリが常に全画面表示されるようにすることで、そのアプリのマルチ ウィンドウ モードを無効にすることもできます。

  • Android 8.0(API レベル 26)では、ピクチャー イン ピクチャー モードが小画面のデバイスに拡張されています。
  • Android 12(API レベル 31)では、マルチウィンドウ モードが標準動作になります。

    画面が大きい(画面幅 600 dp 以上)場合は、アプリの設定にかかわらず、すべてのアプリでマルチウィンドウ モードがサポートされます。resizeableActivity="false" でディスプレイ ディメンションに合わせるために必要な場合は、アプリは互換性モードになります。

    画面が小さい(画面幅 600 dp 未満)場合は、システムによりアクティビティの minWidthminHeight がチェックされ、マルチウィンドウ モードで実行可能かどうかが判断されます。resizeableActivity="false" の場合は、幅と高さの最小値にかかわらず、アプリはマルチウィンドウ モードで実行されません。

    注: デバイス メーカーは、上記の動作をオーバーライドできます。

分割画面モード

ユーザーは次の操作を行うことで、分割画面モードを有効にできます。

  1. 最近の画面を開きます。
  2. スワイプしてアプリをビューに表示します。
  3. アプリのタイトルバーにあるアプリアイコンをタップします。
  4. [分割画面] メニュー オプションを選択します。
  5. 最近の画面から別のアプリを選択するか、最近の画面を閉じて別のアプリを実行します。

ユーザーは、ウィンドウ分割線を画面の上下左右いずれかの端までドラッグすることで、分割画面モードを終了できます。

隣接位置で起動

アプリがインテントを通じてコンテンツにアクセスする必要がある場合は、FLAG_ACTIVITY_LAUNCH_ADJACENT を使用して、隣接する分割画面ウィンドウでコンテンツを開くことができます。

FLAG_ACTIVITY_LAUNCH_ADJACENT は Android 7.0(API レベル 24)で導入され、分割画面モードで実行されているアプリが隣接するウィンドウでアクティビティを起動できるようにしました。

Android 12L(API レベル 32)以降では、このフラグの定義が拡張され、全画面表示を実行するアプリで分割画面モードを有効にしてから、隣接するウィンドウでアクティビティを起動できるようになりました。

隣接するアクティビティを起動するには、FLAG_ACTIVITY_LAUNCH_ADJACENTFLAG_ACTIVITY_NEW_TASK と組み合わせて使用します。次に例を示します。

Kotlin

fun openUrlInAdjacentWindow(url: String) {
    Intent(Intent.ACTION_VIEW).apply {
        data = Uri.parse(url)
        addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or
                           Intent.FLAG_ACTIVITY_NEW_TASK)
    }.also { intent ->
        startActivity(intent)
    }
}

Java

public void openUrlInAdjacentWindow(String url) {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(Uri.parse(url));
    intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
}

マルチ ウィンドウ モードでのアクティビティのライフサイクル

マルチ ウィンドウ モードは、アクティビティのライフサイクルを変更しません。ただし、複数ウィンドウ内でのアプリの再開状態は、Android のバージョンによって異なります。

複数のアプリの再開

Android 10(API レベル 29)以降のバージョンでは、複数のアプリの再開がサポートされています。デバイスがマルチ ウィンドウ モードのときは、すべてのアクティビティが RESUMED 状態のままになります。アクティビティは、別の透明なアクティビティが上にある場合や、フォーカス可能でない場合(ピクチャー イン ピクチャー モードなど)には、一時停止されることもあります。また、通知ドロワーが開いている場合などには、フォーカスされているアクティビティがないということもありえます。onStop メソッドは通常どおりに動作し、アクティビティが画面から外れるたびに呼び出されます。

複数のアプリの再開は、Android 9(API レベル 28)を搭載した一部のデバイスでも使用できます。Android 9 デバイスで複数のアプリの再開を有効にするには、次のマニフェスト メタデータを追加します。

<meta-data android:name="android.allow_multiple_resumed_activities" android:value="true" />

デバイスがこのマニフェスト メタデータをサポートしているかどうかを確認するには、デバイスの仕様をご覧ください。

Android 9

Android 9(API レベル 28)以前のマルチウィンドウ モードでは、ある時点でアクティブなのは、ユーザーが最後に操作したアクティビティのみです。このアクティビティが「最上位」と見なされ、RESUMED 状態の唯一のアクティビティとなります。表示されているその他すべてのアクティビティは STARTED 状態にあり、RESUMED 状態にはありません。ただし、このように再開状態にはないものの表示されているアクティビティには、表示されていないアクティビティよりも高い優先度が与えられます。表示されているアクティビティのいずれかをユーザーが操作すると、そのアクティビティが再開され、その前に最上位だったアクティビティが STARTED 状態になります。

1 つのアクティブ アプリ プロセス内に複数のアクティビティがある場合、Z オーダーが最も高いアクティビティが再開され、他のアクティビティは一時停止されます。

構成の変更

構成の変更を処理するで説明されているように、ユーザーがアプリをマルチウィンドウ モードに切り替えると、構成の変更がアクティビティに通知されます。構成の変更は、ユーザーがアプリのサイズを変更したときや、アプリを全画面モードに戻したときにも通知されます。

基本的に、こうした構成の変更は、デバイスが縦表示から横表示に切り替えられた場合(ただし、デバイスのディメンションが単にスワップされるのでなく変更された場合を除く)にアプリに通知されるのと同じ影響をアクティビティ ライフサイクルに及ぼします。構成の変更を処理するで説明されているように、アクティビティは自身で構成の変更を処理できます。また、アクティビティを破棄して新しいディメンションでアクティビティを再作成するようにシステムに指示することもできます。

ユーザーがウィンドウのサイズを変更して、高さまたは幅を拡大した場合、システムはユーザー操作に合わせてアクティビティのサイズを変更し、必要に応じて構成の変更を実行します。新たにエクスポーズされる領域をアプリが描画するのに時間がかかる場合、その領域は、windowBackground 属性またはデフォルトの windowBackgroundFallback スタイル属性で指定された色で一時的に塗りつぶされます。

リソースへの排他的アクセス

複数のアプリの再開機能をサポートするために、新しいライフサイクル コールバック onTopResumedActivityChanged() が用意されています。

このメソッドは、アクティビティが最上位の再開状態を獲得または喪失したときに呼び出されます。アクティビティがマイクやカメラなどの共有シングルトン リソースを使用している場合は、この状態を把握することが重要です。

Kotlin

override fun onTopResumedActivityChanged(topResumed: Boolean) {
    if (topResumed) {
        // Top resumed activity
        // Can be a signal to re-acquire exclusive resources
    } else {
        // No longer the top resumed activity
    }
}

Java

@Override
public void onTopResumedActivityChanged(boolean topResumed) {
    if (topResumed) {
        // Top resumed activity
        // Can be a signal to re-acquire exclusive resources
    } else {
        // No longer the top resumed activity
    }
}

アプリはその他の理由(共有ハードウェアの取り外しなど)でリソースを失うこともあります。

いずれにしても、アプリは使用可能なリソースに影響するイベントと状態変化にスムーズに対処する必要があります。

カメラを使用するアプリの場合、CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged() を使用することで、カメラにアクセスするタイミングの手がかりが得られます。このメソッドは、Android 10(API レベル 29)以降で使用できます。

resizeableActivity=false はカメラへの排他的アクセスを保証するものではないことにご注意ください。カメラを使用する他のアプリが他のディスプレイで開かれている可能性もあります。

マルチウィンドウ モードのカメラ

図 2. マルチウィンドウ モードのカメラ

アプリがフォーカスを失ったときに、必ずしもカメラを解放する必要はありません。たとえば、新たにフォーカスを得た最上位の再開状態にあるアプリをユーザーが操作している間、カメラのプレビューを継続することもできます。最上位の再開状態にあるアプリでなくても、カメラの実行を続けることに問題はありませんが、切断のケースを適切に処理する必要があります。最上位の再開状態にあるアプリは必要なときにカメラを開くことができます。このとき、それまでカメラを実行していたアプリはアクセスを失います。アプリにフォーカスが戻ると、カメラを再度開くことができます。

アプリが CameraDevice.StateCallback#onDisconnected() コールバックを受け取った後にカメラデバイスを要求すると、CameraAccessException がスローされます。

マルチディスプレイ

Android 10(API レベル 29)は、セカンダリ ディスプレイ上のアクティビティをサポートしています。アクティビティが複数のディスプレイを持つデバイスで実行されている場合、ユーザーはディスプレイ間でアクティビティを移動できます。複数のアプリの再開はマルチスクリーンのシナリオにも適用されます。複数のアクティビティがユーザー入力を同時に受け取ることができます。

アプリは、起動時や別のアクティビティの作成時に、どのディスプレイで実行するかを指定できます。この動作は、マニフェスト ファイルで定義したアクティビティの起動モード、またはアクティビティを起動するエンティティが設定したインテント フラグやオプションによって異なります。詳細については ActivityOptions をご覧ください。

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

構成の変更を処理する場合、アクティビティは onCreateonConfigurationChanged で現在のディスプレイを確認する必要があります。ディスプレイが変更されたときは、リソースとレイアウトを必ず更新するようにします。

アクティビティに対して選択した起動モードで複数のインスタンスが許容されている場合、セカンダリ画面で起動すると、アクティビティの新しいインスタンスが作成されることがあります。この場合、両方のアクティビティが同時に再開されます。

アクティビティの複数のインスタンスが複数のディスプレイに表示される

図 3. アクティビティの複数のインスタンスが複数のディスプレイに表示される

Android 8.0 で導入されたマルチディスプレイ API についてもご確認ください。

アクティビティとアプリのコンテキスト

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

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

リソースとコンテキストは、アクティビティのウィンドウ構成と親ディスプレイによって定義されます。現在のディスプレイは次のように取得します。

Kotlin

val activityDisplay = activity.getDisplay()

Java

Display activityDisplay = activity.getDisplay();

現在のアクティビティのウィンドウ指標は次のように取得します。

Kotlin

val windowMetrics = activity.getWindowManager().getCurrentWindowMetrics()

Java

WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics();

現在のシステム構成の最大ウィンドウ指標は次のように取得します。

Kotlin

val maximumWindowMetrics = activity.getWindowManager().getMaximumWindowMetrics()

Java

WindowMetrics maximumWindowMetrics = activity.getWindowManager().getMaximumWindowMetrics();

最大ウィンドウ指標は、計算、レイアウトの選択、事前に取得するリソースのサイズの決定に役立ちます。これを onCreate() で利用できるようにすると、最初のレイアウトパスの前にこれらの決定を行うことができます。この指標は、特定のビュー要素のレイアウトには使用しないでください。代わりに、Configuration オブジェクトからの情報を使用してください。

ディスプレイ カットアウト

折りたたみ式デバイスでは、折りたたんだときや広げたときにカットアウト ジオメトリが変更されることがあります。カットアウトに関する問題を回避するために、ディスプレイ カットアウトのサポートに関するベスト プラクティスをご確認ください。

セカンダリ ディスプレイ

使用可能なディスプレイは、DisplayManager システム サービスから次のようにして取得できます。

Kotlin

val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val displays = displayManager.getDisplays()

Java

DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
Display[] displays = displayManager.getDisplays();

Display クラスを使用して、ディスプレイのサイズやディスプレイが安全かどうかを示すフラグなど、特定のディスプレイに関する情報を取得します。ただし、ディスプレイのサイズがアプリに割り当てられる表示領域と同じサイズになるとは限りません。マルチウィンドウ モードでは、アプリはディスプレイの一部に配置されることを忘れないでください。

アクティビティをディスプレイ上で起動できるかどうかは、次のようにして判断します。

Kotlin

val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val activityAllowed = activityManager.isActivityStartAllowedOnDisplay(context, displayId, intent)

Java

ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
boolean activityAllowed = activityManager.isActivityStartAllowedOnDisplay(context, displayId, intent);

その後、次のようにしてディスプレイ上でアクティビティを起動します。

Kotlin

val options = ActivityOptions.makeBasic()
options.setLaunchDisplayId(targetDisplay.displayId)
startActivity(intent, options.toBundle())

Java

ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(targetDisplay.displayId);
startActivity(intent, options.toBundle());

マルチディスプレイのサポート

Android は、ソフトウェア キーボード、壁紙、ランチャーをマルチディスプレイでサポートしています。

ソフトウェア キーボード

ディスプレイがシステム デコレーションをサポートするように設定されている場合は、キーボードをセカンダリ画面上に表示できます。そのディスプレイでテキスト フィールドへの入力をリクエストすると、インプット メソッド エディタが自動的に表示されます。

セカンダリ ディスプレイに表示されたキーボード

図 4. セカンダリ ディスプレイに表示されたキーボード

壁紙

Android 10(API レベル 29)では、セカンダリ画面に壁紙を設定できます。ディスプレイごとに WallpaperService.Engine の個別のインスタンスが作成されます。各エンジンのサーフェスが別々に描画されていることをご確認ください。デベロッパーは、WallpaperService.Engine#getDisplayContext() のディスプレイ コンテキストを使用してアセットを読み込むことができます。また、忘れずに WallpaperInfo.xml ファイルで android:supportsMultipleDisplays="true" と設定してください。

スマートフォンとセカンダリ ディスプレイに表示された壁紙

図 5. スマートフォンとセカンダリ ディスプレイに表示された壁紙

ランチャー

新しいインテント フィルタ カテゴリ SECONDARY_HOME は、セカンダリ画面専用のアクティビティを提供します。このアクティビティのインスタンスは、システム デコレーションをサポートするすべてのディスプレイで使用されます(ディスプレイごとに 1 つ)。

<activity>
    ...
    <intent-filter>
        <category android:name="android.intent.category.SECONDARY_HOME" />
        ...
    </intent-filter>
</activity>

アクティビティには、複数のインスタンスの作成を妨げず、さまざまな画面サイズに適応可能な起動モードを設定する必要があります。起動モードを singleInstance または singleTask に設定することはできません。

たとえば、Launcher3 の AOSP 実装は SECONDARY_HOME アクティビティをサポートしています。

スマートフォンに表示されたマテリアル デザイン ランチャー

図 6. スマートフォンに表示されたマテリアル デザイン ランチャー

セカンダリ ディスプレイに表示されたマテリアル デザイン ランチャー

図 7. セカンダリ ディスプレイに表示されたマテリアル デザイン ランチャー

ウィンドウ指標

Android 11(API レベル 30)には、マルチウィンドウ モードで実行されるアプリの境界を提供するために、次の WindowManager メソッドが導入されました。

Jetpack WindowManager ライブラリ メソッドの computeCurrentWindowMetrics()computeMaximumWindowMetrics() はそれぞれ同様の機能を持ちますが、API レベル 14 との下位互換性があります。

現在のディスプレイ以外のディスプレイ指標を取得するには、次のようにします。

  • ディスプレイ コンテキストを作成する
  • ディスプレイのウィンドウ コンテキストを作成する
  • ウィンドウ コンテキストの WindowManager を取得する
  • アプリで使用できる最大ディスプレイ領域の WindowMetrics を取得する

Kotlin

val windowMetrics = context.createDisplayContext(display)
                    .createWindowContext(WindowManager.LayoutParams.TYPE_APPLICATION, null)
                    .getSystemService(WindowManager::class.java)
                    .maximumWindowMetrics

Java

WindowMetrics windowMetrics = context.createDisplayContext(display)
                              .createWindowContext(WindowManager.LayoutParams.TYPE_APPLICATION, null)
                              .getSystemService(WindowManager.class)
                              .getMaximumWindowMetrics();

サポート終了のメソッド

Display メソッドの getSize()getMetrics() は、API レベル 30 でサポートが終了し、新しい WindowManager メソッドに置き換えられました。

Display メソッドの getRealSize()getRealMetrics() は、Android 12(API レベル 31)でサポートが終了し、その動作が getMaximumWindowMetrics() とより厳密に一致するよう更新されています。

マルチ ウィンドウ モードの設定

アプリが Android 7.0(API レベル 24)以降をターゲットにしている場合は、アプリのアクティビティがマルチ ウィンドウ モードをサポートするかどうかと、どのようにサポートするかを設定できます。サイズとレイアウトの両方を制御する属性をマニフェストに設定できます。ルート アクティビティの属性の設定は、タスクスタック内のすべてのアクティビティに適用されます。たとえば、ルート アクティビティに android:resizeableActivity="true" が設定されていると、タスクスタック内のすべてのアクティビティはサイズ変更可能になります。Chromebook などの一部の大型デバイスでは、android:resizeableActivity="false" を指定しても、アプリがサイズ変更可能なウィンドウで実行される場合があります。これによりアプリの動作が阻害される場合は、フィルタを使用することで、そのようなデバイスでのアプリの利用を制限できます。

Android 12(API レベル 31)では、マルチウィンドウ モードがデフォルトです。画面が大きい(画面幅 600 dp 以上)場合は、アプリの設定にかかわらず、すべてのアプリがマルチウィンドウ モードで実行されます。画面が小さい場合は、システムがアクティビティの minWidthminHeightresizeableActivity の設定を確認して、そのアクティビティをマルチウィンドウ モードで実行できるかどうかを判断します。

resizeableActivity

API レベル 30 以下でマルチ ウィンドウ モードを有効または無効にするには、マニフェストの <activity> 要素または <application> 要素でこの属性を次のように設定します。

<application
  android:name=".MyActivity"
  android:resizeableActivity=["true" | "false"] />

この属性を true に設定した場合、アクティビティは分割画面モードまたはフリーフォーム モードで起動できます。この属性を false に設定した場合、アクティビティはマルチウィンドウ モードをサポートしません。この値が false の場合、ユーザーがマルチウィンドウ モードでアクティビティを起動しようとすると、アクティビティは全画面モードで表示されます。

API レベル 24 以降をターゲットとしているアプリで、この属性に値を指定しなかった場合、属性の値はデフォルトで true になります。

API レベル 31 以降をターゲットとしているアプリの場合、この属性による動作は小さい画面と大きい画面で異なります。

  • 大きい画面(画面幅 600 dp 以上): すべてのアプリでマルチウィンドウ モードがサポートされます。この属性は、アクティビティのサイズが変更可能かどうかを示します。resizeableActivity="false" でディスプレイ ディメンションに合わせるために必要な場合は、アプリは画面互換性モードになります。
  • 小さい画面(画面幅 600 dp 未満): resizeableActivity="true" で、アクティビティの幅と高さの最小値がマルチウィンドウの要件内にある場合、アクティビティはマルチウィンドウ モードをサポートします。resizeableActivity="false" の場合、アクティビティは幅と高さの最小値にかかわらず、マルチウィンドウ モードをサポートしません。

supportsPictureInPicture

マニフェストの <activity> ノードでこの属性を設定すると、アクティビティがピクチャー イン ピクチャー モードをサポートするかどうかを指定できます。

<activity
  android:name=".MyActivity"
  android:supportsPictureInPicture=["true" | "false"] />

configChanges

マルチウィンドウの構成の変更(ユーザーがウィンドウ サイズを変更した場合など)を自分で処理するには、android:configChanges 属性をアプリ マニフェストの <activity> ノードに追加して少なくとも次の値を設定します。

<activity
  android:name=".MyActivity"
  android:configChanges="screenSize | smallestScreenSize
      | screenLayout | orientation" />

android:configChanges を追加すると、アクティビティとフラグメントは破棄されて再作成される代わりに、onConfigurationChanged() へのコールバックを受け取ります。その後、手動でビューを更新したり、リソースを再読み込みしたり、必要に応じて他のオペレーションを実行したりできます。

<layout>

Android 7.0(API レベル 24)以降では、<layout> マニフェスト要素で、マルチウィンドウ モードでのアクティビティの動作に影響するいくつかの属性がサポートされています。

android:defaultHeightandroid:defaultWidth
フリーフォーム モードで起動されたときのアクティビティのデフォルトの高さと幅
android:gravity
フリーフォーム モードで起動されたときのアクティビティの初期配置。適切な値については、Gravity をご覧ください。
android:minHeightandroid:minWidth
分割画面モードとフリーフォーム モードの両方におけるアクティビティの高さと幅の最小値。ユーザーが分割画面モードの分割線を動かして、アクティビティを指定された最小サイズより小さくすると、アクティビティはユーザーが要求するサイズに合わせて切り取られます。

次のコードは、アクティビティがフリーフォーム モードで表示されるときに、アクティビティのデフォルトのサイズと位置、および最小サイズを指定する方法を示しています。

<activity android:name=".MyActivity">
    <layout android:defaultHeight="500dp"
          android:defaultWidth="600dp"
          android:gravity="top|end|..."
          android:minHeight="450dp"
          android:minWidth="300dp" />
</activity>

ランタイムのマルチウィンドウ モード

Android 7.0 以降には、マルチウィンドウ モードで実行可能なアプリをサポートする機能が用意されています。

マルチウィンドウ モードで無効になる機能

マルチウィンドウ モードでは、Android は、他のアクティビティまたはアプリとデバイスの画面を共有しているアクティビティに適用されない機能を無効にするか、無視することがあります。

また、一部のシステム UI カスタマイズ オプションが無効になります。たとえば、アプリがマルチ ウィンドウ モードで実行されている場合、ステータスバーを非表示にすることはできません(システム UI の表示を制御するをご覧ください)。

android:screenOrientation 属性に対する変更は無視されます。

マルチ ウィンドウ モードのクエリとコールバック

Activity クラスには、マルチ ウィンドウ モードをサポートする次のメソッドが用意されています。

isInMultiWindowMode()
アクティビティがマルチ ウィンドウ モードかどうかを示します。
isInPictureInPictureMode()

アクティビティがピクチャー イン ピクチャー モードかどうかを示します。

注: ピクチャー イン ピクチャー モードは、マルチウィンドウ モードの特殊なケースです。myActivity.isInPictureInPictureMode() が true を返す場合は、myActivity.isInMultiWindowMode() も true を返します。

onMultiWindowModeChanged()
アクティビティがマルチウィンドウ モードに入るかマルチウィンドウ モードから抜けると、システムは常にこのメソッドを呼び出します。アクティビティがマルチウィンドウ モードに入ったときはこのメソッドに true 値が渡され、アクティビティがマルチウィンドウ モードから抜けたときはこのメソッドに false 値が渡されます。
onPictureInPictureModeChanged()
アクティビティがピクチャー イン ピクチャー モードに入るかピクチャー イン ピクチャー モードから抜けると、システムは常にこのメソッドを呼び出します。アクティビティがピクチャー イン ピクチャー モードに入ったときはこのメソッドに true 値が渡され、アクティビティがピクチャー イン ピクチャー モードから抜けたときはこのメソッドに false 値が渡されます。

上記のメソッドの多くには、Fragment クラスのバージョン(たとえば Fragment.onMultiWindowModeChanged())も用意されています。

ピクチャー イン ピクチャー モード

アクティビティをピクチャー イン ピクチャー モードに切り替えるには、enterPictureInPictureMode() を呼び出します。デバイスがピクチャー イン ピクチャー モードをサポートしていない場合、このメソッドは何の効果も生じさせません。詳細については、ピクチャー イン ピクチャーのサポートをご覧ください。

マルチウィンドウ モードの新しいアクティビティ

新しいアクティビティを起動するとき、新しいアクティビティを現在のアクティビティの隣に(表示できる場合は)表示する必要があるかどうかを指定できます。インテント フラグ FLAG_ACTIVITY_LAUNCH_ADJACENT を使用して、隣接するウィンドウに新しいアクティビティを作成するようシステムに指示し、2 つのアクティビティが画面を共有するようにします。システムはそうするように努めますが、実現できるとは限りません。

デバイスがフリーフォーム モードになっているときに新しいアクティビティを起動する場合は、 ActivityOptions.setLaunchBounds() を呼び出すことにより、新しいアクティビティのディメンションと画面上の位置を指定できます。デバイスがマルチウィンドウ モードになっていない場合、このメソッドは何の効果も生じさせません。

API レベル 30 以前では、タスクスタック内でアクティビティを起動すると、画面上のアクティビティが起動したアクティビティに置き換えられ、すべてのマルチウィンドウ プロパティが継承されます。マルチウィンドウ モードで新しいアクティビティを別のウィンドウとして起動する場合は、そのアクティビティを新しいタスクスタックで起動する必要があります。

Android 12(API レベル 31)では、アプリはタスク ウィンドウを複数のアクティビティに分配できます。アプリによるアクティビティの表示方法(全画面表示、横に並べて表示、縦に並べて表示)を決定するには、XML 構成ファイルを作成するか、Jetpack WindowManager API 呼び出しを行います。

ドラッグ&ドロップ

2 つのアクティビティが画面を共有しているとき、ユーザーは一方のアクティビティからもう一方のアクティビティにデータをドラッグ&ドロップできます(Android 7.0 より前のバージョンでは、同じアクティビティ内でしかデータをドラッグ&ドロップできませんでした)。ドロップされたコンテンツの受け入れに対するサポートを迅速に追加するには、DropHelper API をご覧ください。ドラッグ&ドロップの包括的なガイダンスについては、ドラッグ&ドロップをご覧ください。

マルチインスタンス

各ルート アクティビティは独自のタスクを持ち、独立したプロセスで実行され、独自のウィンドウに表示されます。アプリの新しいインスタンスを別のウィンドウで起動するには、FLAG_ACTIVITY_NEW_TASK フラグを指定して新しいアクティビティを開始します。これとマルチウィンドウ属性のいくつかを組み合わせることで、新しいウィンドウの場所を指定できます。たとえば、ショッピング アプリであれば、商品比較のために複数のウィンドウを表示できます。

Android 12(API レベル 31)では、1 つのアクティビティの 2 つのインスタンスを同じタスク ウィンドウ内に並べて起動できます。

ユーザーがアプリ ランチャーまたはタスクバーからアプリの別のインスタンスを起動できるようにするには、ランチャー アクティビティで android:resizeableActivity="true" を設定し、複数のインスタンスを禁止する起動モードを使用しないようにする必要があります。たとえば、FLAG_ACTIVITY_MULTIPLE_TASK または FLAG_ACTIVITY_NEW_DOCUMENT が設定されている場合、singleInstancePerTask アクティビティを異なるタスクで複数回インスタンス化できます。

マルチインスタンスとマルチパネル レイアウトを混同しないでください。たとえば、SlidingPaneLayout を使用するリスト詳細プレゼンテーションは、単一のウィンドウ内で実行されます。

なお、折りたたみ式デバイスで複数のインスタンスが個別のウィンドウで実行されている場合、形状が変更されると、1 つ以上のインスタンスがバックグラウンドに移動されることがあります。たとえば、デバイスが折りたたまれていない状態で、2 つのアプリ インスタンスが折り目の両側にある 2 つのウィンドウで実行されているとします。デバイスが折りたたまれると、小さくなった画面に合うよう両方のインスタンスが調整される代わりに、片方のインスタンスが終了する可能性があります。

マルチウィンドウ モードの確認

アプリが API レベル 24 以降をターゲットにしているかどうかにかかわらず、ユーザーが Android 7.0 以降を搭載したデバイスをマルチウィンドウ モードにしてアプリを起動しようとする場合に備えて、アプリがマルチウィンドウ モードでどのように動作するかを確認する必要があります。

デバイスをテストする

Android 7.0(API レベル 24)以降を搭載したデバイスは、マルチ ウィンドウ モードをサポートしています。

API レベル 23 以下

アプリが固定された画面の向きを宣言していない限り、ユーザーがマルチ ウィンドウ モードでアプリを使用しようとすると、システムはアプリのサイズを強制的に変更します。

アプリが固定された画面の向きを宣言していない場合は、Android 7.0 以降を搭載したデバイスでアプリを起動し、アプリを分割画面モードにしてみる必要があります。アプリのサイズが強制的に変更されたら、ユーザー エクスペリエンスが許容範囲内かどうかを確認します。

アプリが固定された画面の向きを宣言している場合は、アプリをマルチウィンドウ モードにしてみる必要があります。マルチ ウィンドウ モードにしても、全画面モードのままであることを確認します。

API レベル 24~30

アプリが API レベル 24~30 をターゲットにしており、マルチウィンドウのサポートを無効にしていない場合は、分割画面モードとフリーフォーム モードの両方で次の動作を確認します。

  • アプリを全画面表示で起動した後、最近ボタンを長押ししてマルチウィンドウ モードに切り替えます。アプリのモードが適切に切り替わることを確認します。
  • マルチウィンドウ モードで直接アプリを起動し、アプリが適切に起動されることを確認します。最近ボタンを押した後、アプリのタイトルバーを長押しして、画面上のハイライト表示された領域のいずれかにアプリをドラッグすると、マルチウィンドウ モードでアプリを起動できます。
  • 分割画面モードで分割線をドラッグして、アプリのサイズを変更します。アプリがクラッシュせず、アプリのサイズが変更され、必要な UI 要素が表示されることを確認します。
  • アプリの最小ディメンションを指定している場合は、アプリをそれらのディメンション以下にしてみます。指定した最小ディメンションより小さいサイズに変更できないことを確認します。
  • すべてのテストを通して、アプリのパフォーマンスが許容範囲内かどうかを確認します。たとえば、アプリのサイズを変更したとき、UI の更新に時間がかかりすぎないかを確認します。

API レベル 31 以降

アプリが API レベル 31 以降をターゲットにしており、メイン アクティビティの最小幅と最小高がそれぞれ使用可能な表示領域の幅と高さ以下の場合は、API レベル 24~30 の場合で挙げた動作をすべて確認してください。

テスト チェックリスト

マルチウィンドウ モードでアプリのパフォーマンスを確認するには、以下の操作を試します。特に記載のない限り、分割画面モードとフリーフォーム モードの両方で試す必要があります。

  • マルチウィンドウ モードに入り、マルチウィンドウ モードから抜けます。
  • 一方のアプリからもう一方のアプリに切り替え、最初のアプリが適切に動作する(表示されたままだが、アクティブでなくなる)ことを確認します。たとえば、アプリが動画を再生している場合、ユーザーが別のアプリを操作しているときも動画を再生し続けることを確認します。
  • 分割画面モードで分割線を動かして、アプリの拡大と縮小を試します。アプリを左右に並べて表示しているときと上下に並べて表示しているときの両方で、これらの操作をテストします。アプリがクラッシュせず、基本的な機能が表示され、サイズ変更が速やかに行われることを確認します。
  • いくつかのサイズ変更操作を続けてすばやく行います。アプリがクラッシュせず、メモリリークが発生しないことを確認します。アプリのメモリ使用状況に関する情報は、Android Studio の Memory Profiler で入手できます(Memory Profiler を使用してアプリのメモリ使用量を調べるをご覧ください)。
  • さまざまなウィンドウ構成でアプリを通常どおりに使用し、アプリが適切に動作することを確認します。テキストが判読可能で、UI 要素が操作できないほど小さくならないことを確認します。

マルチウィンドウのサポートを無効にする場合

API レベル 24~30 で android:resizeableActivity="false" を設定してマルチウィンドウ サポートを無効にした場合は、Android 7.0~11 を搭載したデバイスでアプリを起動し、アプリを分割画面モードとフリーフォーム モードにしてみる必要があります。アプリをどちらのモードにしようとしても、全画面モードのままであることを確認します。

参考情報

Android におけるマルチウィンドウ サポートの詳細については、以下をご覧ください。