デベロッパーは、折りたたみ式デバイス(特に、横向きで開く(rotation_0 = 横向き)Samsung Trifold や初代 Google Pixel Fold など)用のアプリを作成する際に、特有の困難に直面することがよくあります。デベロッパーのミスには次のようなものがあります。
- デバイスの向きに関する誤った想定
- 見落とされたユースケース
- 構成の変更をまたいで値を再計算またはキャッシュに保存できない
デバイス固有の問題には、次のようなものがあります。
- カバー ディスプレイとインナー ディスプレイのデバイスの自然な向きが一致しない(rotation_0 = 縦向きに基づく想定)。折りたたみと展開の過程でアプリが失敗する
- 画面密度の違いと density 構成変更の誤った処理
- カメラセンサーが自然な向きに依存していることが原因で発生するカメラ プレビューの問題
折りたたみ式デバイスで高品質のユーザー エクスペリエンスを提供するには、次の重要な領域に焦点を当てます。
- デバイスの物理的な向きではなく、アプリが占有する実際の画面領域に基づいてアプリの向きを決定する
- デバイスの向きとアスペクト比を正しく管理し、横向きのプレビューを回避し、画像が引き伸ばされたり切り取られたりしないように、カメラ プレビューを更新する
ViewModelまたは同様のアプローチで状態を保持するか、画面の密度と向きの変化を手動で処理することで、デバイスの折りたたみまたは展開中にアプリの継続性を維持し、アプリの再起動や状態の損失を回避する- モーション センサーを利用するアプリでは、座標系を調整して画面の現在の向きに合わせ、rotation_0 = 縦向きという前提を回避することで、ユーザーの操作を正確に保証します。
アダプティブ ビルド
アプリがすでにアダプティブで、アダプティブ アプリの品質に関するガイドラインで説明されている最適化レベル(Tier 2)に準拠している場合、アプリは折りたたみ式デバイスで適切に動作します。それ以外の場合は、三つ折りと横向きの折りたたみ式デバイスの詳細を再確認する前に、Android の適応型開発の次の基本的なコンセプトを確認してください。
アダプティブ レイアウト
UI は、さまざまな画面サイズだけでなく、アスペクト比のリアルタイムな変化(折りたたみ式デバイスを広げる、マルチ ウィンドウ モードやデスクトップ ウィンドウ モードに入るなど)にも対応する必要があります。以下の方法について詳しくは、アダプティブ レイアウトについてをご覧ください。
- アダプティブ レイアウトを設計して実装する
- ウィンドウ サイズに基づいてアプリのメイン ナビゲーションを調整する
- ウィンドウ サイズクラスを使用してアプリの UI を調整する
- Jetpack API を使用して、リストと詳細などの正規レイアウトの実装を簡素化
ウィンドウ サイズクラス
横向き折りたたみ式デバイスや三つ折りデバイスなどの折りたたみ式デバイスでは、コンパクト、中程度、拡大幅のウィンドウ サイズクラスを瞬時に切り替えることができます。これらのクラスを理解して実装することで、アプリが現在のデバイスの状態に適したナビゲーション コンポーネントとコンテンツ密度を表示できるようになります。
次の例では、Material 3 アダプティブ ライブラリを使用して、最初に currentWindowAdaptiveInfoV2() 関数を呼び出し、ウィンドウ サイズクラスに対応するレイアウトを使用して、アプリで使用可能なスペースを判断します。
val adaptiveInfo = currentWindowAdaptiveInfoV2()
val windowSizeClass = adaptiveInfo.windowSizeClass
when {
windowSizeClass.isWidthAtLeastBreakpoint(
WIDTH_DP_EXPANDED_LOWER_BOUND
) -> // Expanded
windowSizeClass.isWidthAtLeastBreakpoint(
WIDTH_DP_MEDIUM_LOWER_BOUND
) -> // Medium
else -> // Compact
}
詳しくは、ウィンドウ サイズ クラスを使用するをご覧ください。
アダプティブ アプリの品質
アダプティブ アプリの品質に関するガイドラインの Tier 2(アダプティブ アプリの最適化)または Tier 1(アダプティブ アプリの差別化)に準拠することで、アプリが三つ折りデバイス、横向きの折りたたみ式デバイス、その他の大画面デバイスで魅力的なユーザー エクスペリエンスを提供できるようになります。このガイドラインでは、アダプティブ対応から差別化されたエクスペリエンスへの移行を可能にするため、複数の階層レベルにわたる重要なチェックについて説明します。
Android 16 以降
Android 16(API レベル 36)以上をターゲットとするアプリの場合、システムは、最小幅が 600 dp 以上のディスプレイでの画面の向き、サイズ変更、アスペクト比の制限を無視します。アスペクト比やユーザーが希望する向きに関係なく、アプリはディスプレイ ウィンドウ全体に表示され、レターボックス表示の互換性モードは使用されなくなります。
特記事項
三つ折りと横向きの折りたたみ式デバイスでは、センサー、カメラ プレビュー、構成の継続性(折りたたみ、展開、サイズ変更時の状態の保持)に関して、特別な処理が必要となる独自のハードウェア動作が導入されています。
カメラ プレビュー
横向きの折りたたみ式デバイスやアスペクト比の計算(マルチ ウィンドウ、デスクトップ ウィンドウ、接続されたディスプレイなどのシナリオ)でよくある問題は、カメラ プレビューが引き伸ばされたり、横向きになったり、切り取られたり、回転したりすることです。
前提条件の不一致
この問題は、大画面デバイスや折りたたみ式デバイスでよく発生します。これは、アプリがカメラ機能(アスペクト比やセンサーの向きなど)とデバイス機能(デバイスの向きや自然な向きなど)の間に一定の関係があることを想定しているためです。
新しいフォーム ファクタは、この前提に挑戦しています。折りたたみ式デバイスでは、デバイスの回転を変更せずに、表示サイズとアスペクト比を変更できます。たとえば、デバイスを開くとアスペクト比は変わりますが、ユーザーがデバイスを回転させなければ、回転は同じままです。アスペクト比がデバイスの回転と関連しているとアプリが想定している場合、カメラ プレビューが誤って回転または拡大縮小される可能性があります。アプリがカメラセンサーの向きが縦向きのデバイスの向きと一致することを前提としている場合も同様のことが起こります。横向きの折りたたみ式デバイスでは、必ずしもそうとは限りません。
解決策 1: Jetpack CameraX(推奨)
最もシンプルで堅牢なソリューションは、Jetpack CameraX ライブラリを使用することです。PreviewView UI 要素は、すべてのプレビューの複雑さを自動的に処理するように設計されています。
PreviewViewは、センサーの向き、デバイスの回転、スケーリングを正しく調整します。- 通常は、カメラ画像を中央に配置して切り抜く(FILL_CENTER)ことで、カメラ画像のアスペクト比を維持します。
- 必要に応じて、スケールタイプを
FIT_CENTERに設定してプレビューをレターボックス化できます。
詳しくは、CameraX ドキュメントのプレビューを実装するをご覧ください。
ソリューション 2: CameraViewfinder
既存の Camera2 コードベースを使用している場合は、CameraViewfinder ライブラリ(API レベル 21 との下位互換性あり)も最新のソリューションです。TextureView または SurfaceView を使用してカメラフィードの表示を簡素化し、必要な変換(アスペクト比、スケール、回転)をすべて適用します。
詳しくは、カメラ ビューファインダーの概要のブログ投稿とカメラ プレビューのデベロッパー ガイドをご覧ください。
解決策 3: 手動による Camera2 の実装
CameraX または CameraViewfinder を使用できない場合は、画面の向きとアスペクト比を手動で計算し、構成が変更されるたびに計算が更新されるようにする必要があります。
CameraCharacteristicsからカメラセンサーの向き(0、90、180、270 度など)を取得します。- デバイスの現在のディスプレイの回転を取得します(0、90、180、270 度など)。
- これらの 2 つの値を使用して、
SurfaceViewまたはTextureViewに必要な変換を決定します。 - 歪みを防ぐため、出力
Surfaceのアスペクト比がカメラ プレビューのアスペクト比と一致するようにします。 - カメラアプリは、マルチ ウィンドウ モードまたはデスクトップ ウィンドウ モードで、画面の一部で実行されているか、接続されたディスプレイで実行されている可能性があります。そのため、画面サイズを使用してカメラ ビューファインダーの寸法を決定するべきではありません。代わりにウィンドウ指標を使用してください。
詳しくは、カメラ プレビューのデベロッパー ガイドと、さまざまなフォーム ファクタに対応したカメラアプリの動画をご覧ください。
解決策 4: インテントを使用して基本的なカメラ操作を行う
カメラの機能がそれほど必要ない場合は、デバイスのデフォルトのカメラ アプリケーションで写真や動画の撮影などの基本的なカメラ操作を行うのが簡単な解決策です。カメラ ライブラリと統合する必要はありません。代わりに、インテントを使用します。
詳しくは、カメラ インテントをご覧ください。
構成と連続性
折りたたみ式デバイスは UI の汎用性を高めますが、折りたたみ式でないデバイスよりも多くの構成変更を開始する可能性があります。アプリは、デバイスの回転、折りたたみ/展開、マルチ ウィンドウ モードまたはデスクトップ モードでのウィンドウのサイズ変更など、これらの構成の変更とその組み合わせを管理し、アプリの状態を保持または復元する必要があります。たとえば、アプリは次の継続性を維持する必要があります。
- クラッシュしたり、ユーザーに混乱を招くような変更(画面の切り替えやアプリをバックグラウンドに送るなど)を引き起こしたりすることなく、アプリの状態を保持する
- スクロール可能なフィールドのスクロール位置
- テキスト フィールドに入力されたテキストとキーボードの状態
- 構成の変更が開始されたときに中断した時点から再生が再開されるように、メディアの再生位置
頻繁にトリガーされる構成変更には、screenSize、smallestScreenSize、screenLayout、orientation、density、fontScale、touchscreen、keyboard があります。
android:configChanges と構成の変更に対処するをご覧ください。アプリの状態の管理について詳しくは、UI の状態を保存するをご覧ください。
密度構成の変更
三つ折りデバイスと横向きの折りたたみ式デバイスの外側と内側の画面では、ピクセル密度が異なる場合があります。そのため、density の構成変更の管理には特に注意が必要です。Android では通常、ディスプレイの密度が変更されるとアクティビティが再起動されるため、データが失われる可能性があります。システムによるアクティビティの再起動を防ぐには、マニフェストで密度処理を宣言し、アプリで構成の変更をプログラムで管理します。
AndroidManifest.xml の構成
density: アプリが画面密度変更を処理することを宣言します- その他の構成変更:
screenSize、orientation、keyboardHidden、fontScaleなど、頻繁に発生するその他の構成変更も宣言することをおすすめします。
密度(およびその他の構成変更)を宣言すると、システムはアクティビティを再起動せず、代わりに onConfigurationChanged() を呼び出します。
onConfigurationChanged() の実装
密度が変更された場合は、コールバックでリソースを更新する必要があります(ビットマップの再読み込みやレイアウト サイズの再計算など)。
- DPI が
newConfig.densityDpiに変更されたことを確認します。 - カスタムビューやカスタム ドローアブルなどを新しい密度にリセット
処理するリソース アイテム
- 画像リソース: ビットマップとドローアブルを密度固有のリソースに置き換えるか、スケールを直接調整する
- レイアウト単位(dp から px への変換): ビューのサイズ、マージン、パディングを再計算します。
- フォントとテキストのサイズ: sp 単位のテキストサイズを再適用
- カスタム
View/Canvasの描画:Canvasの描画に使用されるピクセルベースの値を更新
アプリの画面の向きを決定する
アダプティブを構築する際は、物理デバイスの回転に依存しないでください。大画面デバイスでは無視されます。また、マルチウィンドウ モードのアプリは、デバイスとは異なる向きになる可能性があります。代わりに、Configuration.orientation または WindowMetrics を使用して、ウィンドウ サイズに基づいてアプリが現在横向きか縦向きかを特定します。
解決策 1: Configuration.orientation を使用する
このプロパティは、アプリが現在表示されている画面の向きを識別します。
解決策 2: WindowMetrics#getBounds() を使用する
アプリの現在の表示境界を取得し、その幅と高さを確認して向きを判断できます。
スマートフォン(または折りたたみ式デバイスの外部ディスプレイ)ではアプリの向きを制限するが、大画面デバイスでは制限しない必要がある場合は、スマートフォンではアプリの向きを制限するが、大画面では制限しないをご覧ください。
ポスチャーとディスプレイ モード
テーブルトップや HALF_OPENED などの折りたたみ式デバイスの形状と状態は、縦向きの折りたたみ式デバイスと横向きの折りたたみ式デバイスの両方でサポートされます。ただし、三つ折りは卓上姿勢をサポートしておらず、HALF_OPENED として使用することはできません。三つ折りは、完全に開いたときに大画面で独自のユーザー エクスペリエンスを提供します。
HALF_OPENED をサポートする折りたたみ式デバイスでアプリを差別化するには、collectFoldingFeaturesAsState() や currentWindowAdaptiveInfoV2() などの Compose マテリアル 3 アダプティブ API を使用します。
折りたたみ式デバイスの姿勢、状態、カメラ プレビューのサポートについて詳しくは、以下のデベロッパー ガイドをご覧ください。
折りたたみ式デバイスでは、折りたたみ式ならではの視聴エクスペリエンスを実現できます。たとえば、背面カメラ セルフィー プレビューや、外側と内側の画面で異なる表示を同時に行う機能など、背面ディスプレイ モードとデュアル スクリーン モードを活用した特別なディスプレイ機能を構築できます。詳細については、次をご覧ください。
向きを自然なセンサーの向きに固定する
非常に特殊なユースケース(特に、デバイスの折りたたみ状態に関係なく画面全体を占有する必要があるアプリ)では、nosensor フラグを使用すると、アプリをデバイスの自然な向きにロックできます。たとえば、Google Pixel Fold では、折りたたんだ状態のデバイスの自然な向きは縦向きですが、広げた状態の自然な向きは横向きです。nosensor フラグを追加すると、アプリは外側のディスプレイで実行されている場合は縦向きに、インナー ディスプレイで実行されている場合は横向きに強制的にロックされます。
<activity
android:name=".MainActivity"
android:screenOrientation="nosensor">
ゲームと XR センサーのリマッピング
ゲームや XR アプリの場合、センサーの生データ(ジャイロスコープや加速度計など)はデバイス固定の座標系で提供されます。ユーザーがデバイスを回転させてゲームを横向きでプレイすると、センサーの軸が画面に合わせて回転しないため、ゲームの操作が正しく行われません。
この問題を解決するには、現在の Display.getRotation() を確認し、それに応じて軸を再マッピングします。
- Rotation 0: x=x, y=y
- Rotation 90: x=-y, y=x
- Rotation 180: x=-x, y=-y
- 回転 270: x=y、y=-x
回転ベクトル(コンパス アプリや XR アプリで使用)の場合は、SensorManager.remapCoordinateSystem() を使用して、現在の回転に基づいてカメラレンズの方向または画面の上部を新しい軸にマッピングします。
アプリの互換性
すべてのフォーム ファクタと接続されたディスプレイとの互換性を確保するため、アプリはアプリの品質に関するガイドラインに準拠する必要があります。アプリがガイドラインに準拠できない場合、デバイス メーカーは互換性処理を実装できますが、ユーザー エクスペリエンスが低下する可能性があります。
詳細については、プラットフォームで提供されている互換性回避策の包括的なリスト(特に、アプリの動作を変更する可能性があるカメラ プレビュー、オーバーライド、Android 16 API の変更点に関連するもの)をご覧ください。
アダプティブ アプリの作成について詳しくは、アダプティブ アプリの品質に関するガイドラインをご覧ください。