スマートフォンではアプリの向きを制限するが、大画面デバイスでは制限しない

御社のアプリは縦向きのスマートフォンで最適に動作するため、アプリを縦向きのみに制限しているとします。しかし、横向きの大画面ではより多くの操作を行える可能性があります。

両方に対応するには、つまり、小さい画面ではアプリを縦向きに制限し、大画面では横向きを可能にするには、どうすればよいでしょうか。

このガイドは、アプリを改善してすべてのデバイス設定を完全にサポートできるようになるまでの暫定的な対応策です。

アプリの向きを管理する

大画面で横向きを有効にするには、デフォルトで向きの変更を処理するようにアプリ マニフェストを設定します。実行時にアプリのウィンドウ サイズを判別します。アプリ ウィンドウが小さい場合は、マニフェストの向きの設定をオーバーライドして、アプリの向きを制限します。

1. アプリ マニフェストで向きの設定を指定する

アプリ マニフェストの screenOrientation 要素を宣言しないか(この場合、画面の向きはデフォルトで unspecified になります)、画面の向きを fullUser に設定できます。ユーザーがセンサーベースの回転をロックしていない場合、アプリはすべてのデバイスの向きをサポートします。

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

unspecifiedfullUser の違いはわずかながらも重要です。screenOrientation の値を宣言しない場合、システムによって画面の向きが選択されます。画面の向きの定義に使用されるポリシーはデバイスによって異なる場合があります。一方、fullUser を指定すると、ユーザーがデバイスに定義した動作に近くなります。ユーザーがセンサーベースの回転をロックしている場合、アプリはユーザーの設定に従います。それ以外の場合は、4 つある画面の向きのいずれかが許可されます(縦向き、横向き、逆の縦向き、逆の横向き)。screenOrientation をご覧ください。

2. 画面サイズを判別する

ユーザーが許可したすべての向きサポートするようにマニフェストが設定されている場合、画面サイズに基づいてアプリの向きをプログラムで指定することができます。

Jetpack WindowManager ライブラリをモジュールの build.gradle ファイルまたは build.gradle.kts ファイルに追加します。

Kotlin

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Groovy

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

Jetpack WindowManager の WindowMetricsCalculator#computeMaximumWindowMetrics() メソッドを使用して、デバイスの画面サイズを WindowMetrics オブジェクトとして取得します。このウィンドウ指標をウィンドウ サイズクラスと比較すると、画面の向きを制限するタイミングを判断できます。

ウィンドウ サイズクラスは、小さい画面と大画面の間のブレークポイントを提供します。

WindowWidthSizeClass#COMPACT ブレークポイントと WindowHeightSizeClass#COMPACT ブレークポイントを使用して、画面サイズを決定します。

Kotlin

/** Determines whether the device has a compact screen. **/
fun compactScreen() : Boolean {
    val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
    val width = metrics.bounds.width()
    val height = metrics.bounds.height()
    val density = resources.displayMetrics.density
    val windowSizeClass = WindowSizeClass.compute(width/density, height/density)

    return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT ||
        windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT
}

Java

/** Determines whether the device has a compact screen. **/
private boolean compactScreen() {
    WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this);
    int width = metrics.getBounds().width();
    int height = metrics.getBounds().height();
    float density = getResources().getDisplayMetrics().density;
    WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density);
    return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT ||
                windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT;
}
    注:
  • これらの例はアクティビティのメソッドとして実装されているため、アクティビティは computeMaximumWindowMetrics() の引数で this として逆参照されます。
  • アプリがマルチ ウィンドウ モードで起動される可能性があるため、computeCurrentWindowMetrics() の代わりに computeMaximumWindowMetrics() メソッドが使用されています。マルチ ウィンドウ モードの場合、画面の向きの設定は無視されます。アプリ ウィンドウがデバイスの画面全体になっていない限り、アプリ ウィンドウのサイズを判別して向きの設定をオーバーライドしても意味がありません。

アプリ内で computeMaximumWindowMetrics() メソッドを使用できるように依存関係を宣言する手順については、WindowManager をご覧ください。

3. アプリ マニフェストの設定をオーバーライドする

デバイスの画面サイズが小さいと判断した場合は、Activity#setRequestedOrientation() を呼び出してマニフェストの screenOrientation 設定をオーバーライドできます。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    val container: ViewGroup = binding.container

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(object : View(this) {
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
            requestedOrientation = if (compactScreen())
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
                ActivityInfo.SCREEN_ORIENTATION_FULL_USER
        }
    })
}

Java

@Override
protected void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstanceState);
    if (compactScreen()) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
    }
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    ViewGroup container = binding.container;

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(new View(this) {
        @Override
        protected void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            if (compactScreen()) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
            }
        }
    });
}

onCreate() メソッドと View.onConfigurationChanged() メソッドにロジックを追加することで、アクティビティがサイズ変更されたりディスプレイ間を移動されたりするたびに(デバイスの回転後、折りたたみ式デバイスが折りたたまれたとき / 広げられたとき、など)、最大ウィンドウ指標を取得して向きの設定をオーバーライドできるようになります。構成の変更が発生するタイミングや、その変更によってアクティビティの再作成が発生するタイミングについて詳しくは、構成の変更に対処するをご覧ください。

要点

  • screenOrientation: デバイスの向きの変化にアプリがどう反応するかを指定できるアプリ マニフェストの設定
  • Jetpack WindowManager: アプリ ウィンドウのサイズとアスペクト比を判別できるライブラリのセット。API レベル 14 との下位互換性がある
  • Activity#setRequestedOrientation(): 実行時にアプリの向きを変更できるメソッド

結果

アプリは、デバイスの回転に関係なく、小さい画面では縦向きのままになります。大画面では横向きと縦向きをサポートします。

このガイドを含むコレクション

このガイドは、Android 開発の幅広い目標を網羅する、厳選されたクイックガイド コレクションの一部です。

タブレット、折りたたみ式デバイス、ChromeOS デバイスで最適化されたユーザー エクスペリエンスをサポートするようにアプリを有効にします。

ご質問やフィードバックがある場合

よくある質問のページでクイックガイドをご覧になるか、お問い合わせフォームからご意見をお寄せください。