デベロッパーは、折りたたみ式デバイス(特に Samsung Trifold や初代 Google Pixel Fold など、横向きで開くデバイス(rotation_0 = 横向き))用のアプリを作成する際に、特有の困難に直面することがよくあります。デベロッパーが犯しがちな間違いは次のとおりです。
- デバイスの向きに関する誤った想定
- 見落とされたユースケース
- 構成の変更をまたいで値を再計算またはキャッシュに保存しない
デバイス固有の問題としては、次のようなものがあります。
- カバー ディスプレイとインナー ディスプレイのデバイスの自然な画面の向きが一致しない(rotation_0 = 縦向きに基づく想定)ため、折りたたみ / 展開の操作でアプリが失敗する
- 画面密度が異なり、 density 構成の変更処理が正しく行われない
- カメラセンサーが自然な向きに依存しているために発生するカメラ プレビューの問題
折りたたみ式デバイスで高品質のユーザー エクスペリエンスを提供するには、次の重要な領域に焦点を当てます。
- アプリの向きは、デバイスの物理的な向きではなく、アプリが占有する実際の画面領域に基づいて決定する
- カメラ プレビューを更新して、デバイスの画面の向きとアスペクト比を正しく管理し、横向きのプレビューを回避し、画像が引き伸ばされたり切り取られたりしないようにする
- `
ViewModel` または同様の方法で状態を保持するか、画面密度と画面の向きの変更を手動で処理することで、デバイスの折りたたみ / 展開時にアプリの継続性を維持する。これにより、アプリの再起動や状態の損失を回避できる - モーション センサーを使用するアプリの場合は、座標系を画面の現在の向きに合わせて調整し、rotation_0 = 縦向きに基づく想定を回避して、正確なユーザー操作を保証する
アダプティブ アプリを作成する
アプリがすでにアダプティブであり、最適化レベル(ティア 2) アダプティブ アプリの品質に関するガイドラインで概説されているレベルに準拠している場合、アプリは折りたたみ式デバイスで 適切に機能します。そうでない場合は、Trifold と横向きの折りたたみ式デバイスの詳細を再確認する前に、Android のアダプティブ開発の基礎となる次のコンセプトを確認してください。
アダプティブ レイアウト
UI は、さまざまな画面サイズだけでなく、アスペクト比のリアルタイムな変化(展開、マルチウィンドウ モードまたはデスクトップ ウィンドウ モードへの移行など)にも対応する必要があります。方法について詳しくは、アダプティブ レイアウトについてをご覧ください。
- アダプティブ レイアウトを設計して実装する
- ウィンドウ サイズに基づいてアプリのメイン ナビゲーションを調整する
- ウィンドウ サイズクラスを使用してアプリの UI を調整する
- Jetpack API を使用して、リストと詳細などの正規レイアウトの実装を簡素化する
ウィンドウ サイズクラス
横向きの折りたたみ式デバイスや Trifold などの折りたたみ式デバイスは、コンパクト、ミディアム、拡大幅のウィンドウ サイズクラスを瞬時に切り替えることができます。これらのクラスを理解して実装することで、アプリは現在のデバイスの状態に適した Navigation コンポーネントとコンテンツ密度を表示できます。
次の例では、Material 3 アダプティブ ライブラリを使用して、アプリが使用できるスペースの量を判断します。まず、
currentWindowAdaptiveInfo() 関数を呼び出し、次に 3 つのウィンドウ サイズクラスに対応する
レイアウトを使用します。
val adaptiveInfo = currentWindowAdaptiveInfo()
val windowSizeClass = adaptiveInfo.windowSizeClass
when {
windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_EXPANDED_LOWER_BOUND) -> // Expanded
windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND) -> // Medium
else -> // Compact
}
詳しくは、ウィンドウ サイズクラスを使用するをご覧ください。
アダプティブ アプリの品質
アダプティブ アプリの品質に関するガイドラインのティア 2(アダプティブ最適化) またはティア 1(アダプティブ 差別化)に準拠することで、アプリは Trifold デバイス、横向きの折りたたみ式デバイス、その他の大画面デバイスで魅力的なユーザー エクスペリエンスを提供できます。このガイドラインでは、アダプティブ対応から差別化されたエクスペリエンスに移行するために、複数のティアレベルで重要なチェックについて説明しています。
Android 16 以降
Android 16(API レベル 36)以上をターゲットとするアプリの場合、最小幅が 600 dp 以上のディスプレイでは、画面の向き、サイズ変更、アスペクト比の制約は無視されます。アプリは、アスペクト比やユーザーが希望する向きに関係なく、ディスプレイウィンドウ全体に表示され、レターボックス表示互換モードは使用されなくなります。
その他の考慮事項
Trifold と横向きの折りたたみ式デバイスには、センサー、カメラ プレビュー、構成の継続性(折りたたみ、展開、サイズ変更時の状態の保持)に関して、特別な処理が必要となる独自のハードウェア動作があります。
カメラ プレビュー
横向きの折りたたみ式デバイスやアスペクト比の計算(マルチ ウィンドウ、デスクトップ ウィンドウ、接続されたディスプレイなどのシナリオ)でよくある問題は、カメラ プレビューが引き伸ばされたり、横向きになったり、切り取られたり、回転したりすることです。
想定の不一致
この問題は、大画面デバイスや折りたたみ式デバイスでよく発生します。これは、アプリがカメラ機能(アスペクト比やセンサーの向きなど)とデバイス機能(デバイスの向きや自然な向きなど)の間に一定の関係があることを想定しているためです。
新しいフォーム ファクタでは、この想定が課題となります。折りたたみ式デバイスでは、デバイスを回転させずに表示サイズとアスペクト比を変更できます。たとえば、デバイスを展開するとアスペクト比が変わりますが、ユーザーがデバイスを回転させない限り、回転は変わりません。アプリがアスペクト比とデバイスの回転が相関していると想定している場合、カメラ プレビューが誤って回転または拡大縮小される可能性があります。アプリがカメラセンサーの向きが縦向きのデバイスの向きと一致すると想定している場合も同様のことが起こります。これは、横向きの折りたたみ式デバイスでは必ずしも当てはまりません。
解決策 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 の状態を保存するをご覧ください。
密度構成の変更
Trifold と横向きの折りたたみ式デバイスの外側と内側の画面では、ピクセル密度が異なる場合があります。そのため、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 などの折りたたみ式デバイスの形状と状態は、縦向きの折りたたみ式デバイスと横向きの折りたたみ式デバイスの両方で
サポートされています。ただし、Trifold
はテーブルトップの形状をサポートしておらず、HALF_OPENED
で使用することはできません。代わりに、Trifold は完全に展開したときに、ユニークなユーザー エクスペリエンスを実現する大きな画面を提供します。
HALF_OPENED をサポートする折りたたみ式デバイスでアプリを差別化するには、Jetpack
WindowManager API(FoldingFeature など)を使用します。
折りたたみ式デバイスの形状、状態、カメラ プレビューのサポートについて詳しくは、次のデベロッパー ガイドをご覧ください。
折りたたみ式デバイスでは、折りたたみ式ならではの視聴エクスペリエンスを実現できます。背面ディスプレイ モードとデュアル スクリーン モードを活用すると、背面カメラ セルフィー プレビューや、内側と外側の画面で異なる表示を同時に行う機能など、折りたたみ式デバイス向けの特別なディスプレイ機能を構築できます。詳しくは、以下をご覧ください。
向きを自然なセンサーの向きに固定する
非常に特殊なユースケース(特に、デバイスの折りたたみ状態に関係なく画面全体を占有する必要があるアプリ)では、nosensor フラグを使用すると、アプリをデバイスの自然な画面の向きに固定できます。たとえば、Google Pixel Fold では、折りたたみ時のデバイスの自然な向きは縦向きですが、展開時の自然な向きは横向きです。nosensor フラグを追加すると、外側のディスプレイで実行されている場合はアプリが縦向きに固定され、インナー ディスプレイで実行されている場合は横向きに固定されます。
<activity
android:name=".MainActivity"
android:screenOrientation="nosensor">
ゲームと XR センサーのリマッピング
ゲームアプリと XR アプリの場合、生のセンサーデータ(ジャイロスコープや加速度センサーなど)は、デバイス固定の座標系で提供されます。ユーザーがデバイスを回転させて横向きでゲームをプレイすると、センサー軸が画面に合わせて回転しないため、ゲームの操作が正しく行われません。
この問題を修正するには、現在の Display.getRotation() を確認し、それに応じて 軸を再マッピングします。
- 回転 0: x=x, y=y
- 回転 90: x=-y, y=x
- 回転 180: x=-x, y=-y
- 回転 270: x=y, y=-x
回転ベクトル(コンパスアプリや XR アプリで使用)の場合は、 SensorManager.remapCoordinateSystem()を使用して、現在の回転に基づいてカメラレンズの方向または 画面の上部を新しい軸にマッピングします。
アプリの互換性
アプリは、すべてのフォーム ファクタと接続されたディスプレイで互換性を確保するために、アプリの品質に関するガイドラインに準拠する必要があります。アプリがガイドラインに準拠できない場合、デバイス メーカーは互換性処理を実装できますが、ユーザー エクスペリエンスが低下する可能性があります。
詳しくは、プラットフォームで提供されている互換性の 回避策の包括的なリストをご覧ください。特に、カメラ プレビュー、オーバーライド、アプリの動作を 変更する可能性があるAndroid 16 API の変更に関連する回避策をご覧ください。
アダプティブ アプリの作成について詳しくは、アダプティブ アプリの品質 に関するガイドラインをご覧ください。