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

Android 7.0 では、複数のアプリを同時に表示するためのサポートが追加されました。ハンドヘルド デバイスでは、分割画面モードで 2 つのアプリを左右または上下に並べて表示できます。TV デバイスでは、アプリがピクチャー イン ピクチャー モードで動画を再生している間、ユーザーは別のアプリを操作できます。

Android 7.0(API レベル 24)以降をターゲットにしているアプリでは、アプリがマルチウィンドウ ディスプレイを処理する方法を構成できます。たとえば、アクティビティの最小許容ディメンションを指定できます。また、アプリに対してマルチウィンドウ ディスプレイを無効にし、アプリを全画面モードのみで表示するようシステムに指示することもできます。

概要

Android では、複数のアプリが画面を同時に共有できます。たとえば、ユーザーは画面を分割して、ウェブページを左側の画面に表示したまま、右側の画面でメールを作成できます。ユーザー エクスペリエンスは、Android OS のバージョンとデバイスの種類によって異なります。

  • Android 7.0 を搭載したハンドヘルド デバイスは、分割画面モードに対応しています。このモードでは、2 つのアプリを左右または上下に並べて画面に表示できます。ユーザーは、2 つのアプリ画面の分割線をドラッグして、一方のアプリを拡大し、もう一方のアプリを縮小できます。
  • Android 8.0 以降では、アプリをピクチャー イン ピクチャー モードにすると、そのアプリにコンテンツを表示したまま、他のアプリをブラウジングまたは操作できます。
  • 大型画面デバイスのメーカーは、ユーザーが各アクティビティのサイズを自由に変更できるフリーフォーム モードを有効にすることもできます。メーカーがこの機能を有効にした場合、デバイスでは、分割画面モードに加えて、フリーフォーム モードを利用できます。

図 1. 分割画面モードで左右に並べて実行されている 2 つのアプリ

ユーザーは、次の方法でマルチウィンドウ モードに切り替えることができます。

  • オーバービュー画面を開き、アクティビティ タイトルを長押しすると、そのアクティビティを画面のハイライト表示された領域にドラッグして、アクティビティをマルチウィンドウ モードに切り替えることができます。
  • オーバービュー ボタンを長押しすると、現在のアクティビティがマルチウィンドウ モードになり、オーバービュー画面が開いて、画面を共有する別のアクティビティの選択が可能になります。

複数のアクティビティが画面を共有しているとき、ユーザーは一方のアクティビティからもう一方のアクティビティにデータをドラッグ&ドロップできます。

マルチウィンドウのライフサイクル

マルチウィンドウ モードは、アクティビティのライフサイクルを変更しません。

マルチウィンドウ モードでは、ユーザーが直前に操作したアクティビティのみがその時点でアクティブになります。このアクティビティはトップレベルにあると見なされます。RESUMED 状態にあるのは、このアクティビティだけです。 表示されているその他すべてのアクティビティは STARTED 状態にあり、RESUMED 状態にはありません。しかし、表示されているが再開されていないこれらのアクティビティには、表示されていないアクティビティよりも高い優先度が与えられます。表示されているアクティビティのいずれかをユーザーが操作すると、そのアクティビティが再開され、その前にトップレベルだったアクティビティが STARTED 状態になります。

注: マルチウィンドウ モードでは、アプリがユーザーに表示されていても RESUMED 状態でないことがあります。場合によっては、アプリはトップレベルでないときもオペレーションを続行する必要があります。たとえば、この状態の動画再生アプリは、動画を表示し続けなければなりません。そのため、動画を再生するアクティビティが ON_PAUSE ライフサイクル イベントに応じて動画を一時停止しないようにすることをおすすめします。代わりに、アクティビティが ON_START に応じて再生を開始し、ON_STOP に応じて再生を一時停止するようにします。Lifecycle パッケージを使用せずに直接ライフサイクル イベントを処理する場合は、onStop() ハンドラで動画の再生を一時停止し、onStart() で再生を再開します。

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

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

アプリがマルチウィンドウ モードで実行されている場合、再開状態はデバイスの Android バージョンによって異なります。

  • Android 9(API レベル 28)以前の場合、フォーカスされているアクティビティのみが RESUMED 状態になり、他はすべて PAUSED になります。1 つのアプリプロセス内に複数のアクティビティがある場合、Z オーダーが最も高いアクティビティが RESUMED になり、他は PAUSED になります。
  • Android 10(API レベル 29)以降の場合、表示されている最上部のフォーカス可能なアクティビティはすべて RESUMED になります。

詳しくは、複数のアプリの再開に関するドキュメントをご覧ください。

マルチウィンドウ モード向けにアプリを構成する

アプリが API レベル 24 以降をターゲットにしている場合は、アプリのアクティビティがマルチウィンドウ ディスプレイをサポートする方法と、サポートするかどうかを構成できます。サイズとレイアウトの両方を制御する属性をマニフェストに設定できます。 ルート アクティビティの属性の設定は、タスクスタック内のすべてのアクティビティに適用されます。たとえば、ルート アクティビティで android:resizeableActivity を true に設定すると、タスクスタック内のすべてのアクティビティはサイズ変更が可能になります。

注: API レベル 23 以前をターゲットとするマルチオリエンテーション アプリをビルドした場合、ユーザーがそのアプリをマルチウィンドウ モードで使用すると、システムは強制的にアプリのサイズを変更します。さらに、アプリが予想外の動作をする可能性があることをユーザーに警告するダイアログ ボックスを表示します。システムは、画面の向きが固定されたアプリのサイズを変更しません。画面の向きが固定されたアプリをユーザーがマルチウィンドウ モードで開こうとすると、アプリは全画面モードで表示されます。

Chromebook などの一部の大型デバイスでは、android:resizeableActivity=”false” を指定しても、アプリがサイズ変更可能なウィンドウで実行される場合があります。これによりアプリの動作が阻害される場合は、フィルタを使用することで、そのようなデバイスでのアプリの利用を制限できます。

android:resizeableActivity

マルチウィンドウ ディスプレイを有効または無効にするには、マニフェストの <activity> 要素または <application> 要素でこの属性を設定します。

android:resizeableActivity=["true" | "false"]

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

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

android:supportsPictureInPicture

マニフェストの <activity> ノードでこの属性を設定すると、アクティビティがピクチャー イン ピクチャー ディスプレイをサポートするかどうかを指定できます。android:resizeableActivity が false の場合、この属性は無視されます。

android:supportsPictureInPicture=["true" | "false"]

レイアウト属性

Android 7.0 では、<layout> マニフェスト要素により、マルチウィンドウ モードでのアクティビティの動作に影響を及ぼすいくつかの属性がサポートされるようになりました。

android:defaultWidth
フリーフォーム モードで起動されたときのアクティビティのデフォルトの幅。
android:defaultHeight
フリーフォーム モードで起動されたときのアクティビティのデフォルトの高さ。
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:configChanges 属性をマニフェストに追加して、少なくとも次の値を指定します。

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

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

マルチウィンドウ モードでアプリを実行する

Android 7.0 以降は、マルチウィンドウ モードで実行できるアプリをサポートする機能がシステムによって提供されるようになりました。

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

デバイスがマルチウィンドウ モードになっているとき、一部の機能は無効にされるか無視されます。それらの機能は、他のアクティビティまたはアプリとデバイスの画面を共有する可能性があるアクティビティでは意味をなさないためです。そうした機能には次のようなものがあります。

  • システム UI の一部のカスタマイズ オプションは無効になります。たとえば、アプリが全画面モードで実行されていない場合、アプリはステータスバーを非表示にできません。
  • android:screenOrientation 属性に対する変更はシステムにより無視されます。

マルチウィンドウの変更通知とクエリ

Activity は、マルチウィンドウ ディスプレイをサポートする次のメソッドを提供します。

isInMultiWindowMode()
アクティビティがマルチウィンドウ モードで実行されているかどうかを判別する際に呼び出します。
isInPictureInPictureMode()
アクティビティがピクチャー イン ピクチャー モードで実行されているかどうかを判別する際に呼び出します。

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

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

Fragment クラスは、こうしたメソッドの多くのバージョン(たとえば Fragment.onMultiWindowModeChanged())をエクスポーズします。

ピクチャー イン ピクチャー モードに切り替える

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

マルチウィンドウ モードで新しいアクティビティを起動する

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

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

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

ドラッグ&ドロップをサポートする

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

DragAndDropPermissions
ドロップ先のアプリに付与される権限を指定するトークン オブジェクト。
View.startDragAndDrop()
View.startDrag() のエイリアス。アクティビティ間のドラッグ&ドロップを有効にするには、DRAG_FLAG_GLOBAL フラグを渡します。ドロップ先のアクティビティに URI 権限を付与する必要がある場合は、権限に応じて DRAG_FLAG_GLOBAL_URI_READ フラグまたは DRAG_FLAG_GLOBAL_URI_WRITE フラグを渡します。
View.cancelDragAndDrop()
現在進行中のドラッグ オペレーションをキャンセルします。ドラッグ オペレーションを開始した側のアプリだけが呼び出せます。
View.updateDragShadow()
現在進行中のドラッグ オペレーションのドラッグ シャドウを置き換えます。ドラッグ オペレーションを開始した側のアプリだけが呼び出せます。
Activity.requestDragAndDropPermissions()
DragEvent に含まれる ClipData で渡されるコンテンツ URI の権限をリクエストします。
DragAndDropPermissions.release()
ClipData で指定されたコンテンツ URI のデータにアクセスするために必要な権限を解放します。このメソッドを呼び出さなかった場合は、権限を含むアクティビティが破棄されたときに、自動的に権限が解放されます。

マルチインスタンス

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

マルチインスタンスとマルチパネル レイアウト(SlidingPaneLayout を使用するリストやディテール レイアウトなど)を混同しないでください。マルチパネル レイアウトは 1 つのウィンドウ内で実行されます。

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

アプリのマルチウィンドウ サポートをテストする

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

テストデバイスの設定

Android 7.0 以降を搭載したデバイスでは、分割画面モードが自動的にサポートされます。

アプリが API レベル 23 以前をターゲットにしている場合

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

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

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

マルチウィンドウ モードをサポートしている場合

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

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

テスト チェックリスト

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

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

マルチウィンドウ サポートを無効にした場合

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

Android におけるマルチウィンドウ サポートの詳細については、Android N でマルチウィンドウを準備するための 5 つのヒントマルチウィンドウ プレイグラウンド サンプルアプリを参照してください。