Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

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

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

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

概要

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

  • Android 7.0 を実行しているハンドヘルド端末は、分割画面モードに対応しています。このモードでは、画面で 2 つのアプリが実行され、これらのアプリを並べて、または重ねて表示できます。ユーザーは、2 つのアプリを分離している分割線をドラッグして、1 つのアプリを拡大し、もう 1 つのアプリを縮小することができます。
  • Android 8.0 以降では、アプリをピクチャー イン ピクチャー モードにすると、アプリにコンテンツを表示したまま、他のアプリをブラウジングまたは操作できます。
  • より大きい画面の端末のメーカーは、ユーザーが各アクティビティのサイズを自由に変更できるフリーフォーム モードを有効にすることもできます。メーカーがこの機能を有効にした場合、端末では、分割画面モードに加えて、フリーフォーム モードが利用できます。

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

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

  • ユーザーは [Overview] 画面を開いているときに、アクティビティのタイトルを長押ししてから、そのアクティビティを画面のハイライト表示された部分にドラッグすることにより、アクティビティをマルチウィンドウ モードにすることができます。
  • ユーザーが [Overview] ボタンを長押しすると、現在のアクティビティがマルチウィンドウ モードになり、[Overview] 画面が開くので、ユーザーは、画面で共有する別のアクティビティを選択できるようになります。

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

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

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

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

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

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

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

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

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

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

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 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 つのアクティビティが画面を共有しているときに、データを 1 つのアクティビティから別のアクティビティにドラッグ&ドロップできます(Android 7.0 より前は、単一のアクティビティ内でのみデータをドラッグ&ドロップできました)。そのため、アプリがドラッグ&ドロップをサポートしていない場合は、ドラッグ&ドロップ機能をアプリに追加する必要があります。

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 のパーミッションをリクエストします。

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

ユーザーが Android 7.0 以降を実行している端末をマルチウィンドウ モードにしてアプリの起動を試みる場合に備えて、アプリが API レベル 24 以上を対象にしているかどうかに関係なく、アプリがマルチウィンドウ モードでどのように動作するかを確認する必要があります。

テスト端末の構成

端末で Android 7.0 以降が実行されている場合は、分割画面モードが自動的にサポートされます。

アプリが API レベル 23 以前を対象としている場合

アプリが API レベル 23 以前を対象としており、ユーザーがマルチウィンドウ モードでそのアプリを使用しようとすると、アプリが画面の向きの固定を宣言しない限り、アプリのサイズが強制的に変更されます。

アプリが画面の向きの固定を宣言しない場合、Android 7.0 以降を実行している端末でアプリを起動し、アプリを分割画面モードにすることを試みる必要があります。アプリのサイズが強制的に変更されたときに、ユーザー エクスペリエンスが許容範囲内にあることを確認してください。

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

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

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

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

チェックリストのテスト

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

  • マルチウィンドウ モードを有効にしてから無効にします。
  • 1 つのアプリから別のアプリに切り替えたとき、最初のアプリの動作が適切であり、表示されているが、アクティブになっていないことを確認します。たとえば、アプリが動画を再生している場合、ユーザーが別のアプリを操作しているときに、アプリで動画の再生が継続していることを確認します。
  • 分割画面モードで分割線を移動して、アプリの拡大および縮小を試みてください。アプリを並べて表示しているときと重ねて表示しているときの両方でこれらの操作を試みてください。アプリがクラッシュせず、基本的な機能が表示され、サイズ変更の操作に時間がかかりすぎないことを確認します。
  • 続けざまにサイズ変更の操作を行います。アプリがクラッシュしたり、メモリリークが発生したりしないことを確認します。アプリのメモリ使用量の確認については、Android Studio の Memory Profiler を使用してください。
  • さまざまなウィンドウ構成でアプリを通常の方法で使用し、アプリが適切に動作することを確認します。テキストが読み取り可能であり、UI 要素が操作に支障をきたすほど小さくないことを確認します。

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

android:resizeableActivity="false" を設定して、マルチウィンドウのサポートを無効にした場合は、Android 7.0 以降を実行している端末でアプリを起動し、アプリをフリーフォーム モードおよび分割画面モードにすることを試みる必要があります。そのときに、アプリが全画面モードのままであることを確認してください。

Android でのマルチウィンドウのサポートの詳細については、Android N でマルチウィンドウを準備するための 5 つのヒントおよびマルチウィンドウ Playground サンプルアプリをご覧ください。