注: このページでは、Camera2 パッケージについて説明します。アプリで Camera2 の特定の低レベルの機能を必要とする場合を除き、CameraX を使用することをおすすめします。CameraX と Camera2 は、どちらも Android 5.0(API レベル 21)以降に対応しています。
Android でカメラとカメラ プレビューの向きが常に同じではない できます。
カメラは、デバイスがスマートフォン、タブレット、パソコンのいずれであるかにかかわらず、デバイス上の固定位置にあります。デバイスの向きが変わると、 カメラの向きが変わります。
そのため、カメラアプリは通常、カメラアプリ デバイスの向きとカメラ プレビューのアスペクト比。特定の スマートフォンが縦向きになっているため、カメラ プレビューは縦長になる 幅を狭めると長くなりますスマートフォン(とカメラ)を横向きにすると、カメラ プレビューは縦よりも横が長くなります。
しかし、折りたたみ式デバイスなどの新しいフォーム ファクタや、マルチウィンドウやマルチディスプレイなどの表示モードでは、これらの前提が当てはまりません。折りたたみ式デバイスでは、向きを変更せずにディスプレイのサイズとアスペクト比を変更できます。マルチウィンドウ モードでは、カメラアプリは画面の一部に制限され、デバイスの向きに関係なくカメラ プレビューがスケーリングされます。マルチディスプレイ モードでは、セカンダリ ディスプレイを使用できますが、 プライマリ ディスプレイと同じ向きにする必要があります。
カメラの向き
Android 互換性定義では、カメラのイメージ センサーの向きは、カメラの長辺と画面の長辺が平行となる向きにしなければならないと規定されています。つまり、デバイスが横向きで保持されている場合、カメラは横向きで画像をキャプチャしなければなりません。これは、デバイスの自然な状態にかかわらず、 向き:つまり、横向き主体のデバイスだけでなく、 自動的に選択されます。
カメラから画面への配置により、カメラの表示領域が最大化される ビューファインダーの画面ですまた、イメージ センサーは通常、 アスペクト比は 4:3 が一般的です

カメラセンサーの自然な向きは横向きです。図 1 のセンサーは、 カメラは前面カメラと同じ方向を向いているため、 がスマートフォンに対して 270 度回転している Android 互換性定義。
センサーの回転をアプリに公開するため、camera2 API には SENSOR_ORIENTATION
定数が含まれています。ほとんどのスマートフォンとタブレットでは、デバイスは前面カメラのセンサーの向きを 270 度、背面カメラのセンサーの向きを 90 度(デバイスの背面からの視点)と報告します。これにより、センサーの長辺がデバイスの長辺と揃います。ノートパソコンのカメラは通常、
0 度または 180 度に設定します。
カメラのイメージ センサーは、データ(イメージ バッファ)を
センサーの自然な向き(横向き)では、画像バッファを
カメラ プレビューの角度(SENSOR_ORIENTATION
で指定された角度)
デバイスの自然な向きで縦向きに表示できます。前面カメラの場合は
回転は反時計回りです。背面カメラの場合は時計回りで設定できます
たとえば、図 1 の前面カメラの場合、画像バッファは 次のような画像が表示されます。

プレビューの向きがデバイスの向きと一致するように、画像を反時計回りに 270 度回転する必要があります。

背面カメラでは、同じ向きの画像バッファが生成されます。
上記のバッファと同じですが、SENSOR_ORIENTATION
は 90 度です。その結果、バッファは時計回りに 90 度回転します。
デバイスの回転
デバイスの回転は、デバイスが自然な向きから回転した度数です。たとえば、横向きのスマートフォンには、 方向に応じて 90 度または 270 度回転できます。
カメラセンサーのイメージ バッファは、カメラセンサーのイメージ バッファを 回転(センサーの向きに加えて)が回転するように カメラ プレビューが上向きに表示されます。
向きの計算
カメラ プレビューの適切な向きは、センサーの向きとデバイスの回転を考慮して決定されます。
センサー イメージ バッファの全体的な回転は、 式:
rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360
ここで、sign
は、前面カメラの場合は 1
、背面カメラの場合は -1
です。
前面カメラの場合、画像バッファは反時計回りに回転します( センサーの自然な向き)が調整されます。背面カメラの場合、センサー画像バッファは時計回りに回転します。
式 deviceOrientationDegrees * sign + 360
は、背面カメラのデバイスの回転を反時計回りから時計回りに変換します(たとえば、反時計回りの 270 度を時計回りの 90 度に変換します)。モジュロ演算は、結果を 360 度未満にスケーリングします(たとえば、540 度の回転を 180 度にスケーリングします)。
API によってデバイスの回転の報告方法は異なります。
Display#getRotation()
は、デバイスを反時計回りに回転します(ユーザーの視点から)。この値は、上記の式にそのまま挿入します。OrientationEventListener#onOrientationChanged()
は、ユーザーの視点から見たデバイスの時計回りの回転を返します。 上記の式で使用する値を否定します。
前面カメラ

図 2 のカメラセンサーによって生成された画像バッファは次のとおりです。

センサーの向きに合わせて調整するには、バッファを反時計回りに 270 度回転する必要があります(上記のカメラの向きを参照)。

次に、デバイスの回転を考慮してバッファを反時計回りに 90 度回転させます。これにより、図 2 のようにカメラ プレビューの向きが正しくなります。

ここでは、カメラを右を横向きにしています。

画像バッファは次のとおりです。

センサーを調整するには、バッファを反時計回りに 270 度回転する必要があります 向き:

次に、バッファを反時計回りにさらに 270 度回転させ、 次のように指定します。

背面カメラ
背面カメラのセンサーの向きは通常 90 度です(デバイスの背面から見た場合)。カメラ プレビューの向きを調整する際、 センサー イメージ バッファは、センサーの回転量だけ時計回りに回転しています (前面カメラのように反時計回りではなく)してから、画像を バッファがデバイスの回転量だけ反時計回りに回転します。

図 4 のカメラセンサーからの画像バッファは次のとおりです。

センサーを調整するには、バッファを時計回りに 90 度回転する必要があります 向き:

次に、デバイスを考慮して、バッファを反時計回りに 270 度回転させます。 ローテーション:

アスペクト比
デバイスの画面の向きが変わるとディスプレイのアスペクト比が変化するだけでなく、 マルチウィンドウでウィンドウのサイズが変更されたときに、折りたたみ式デバイスでの折りたたみと展開 セカンダリ ディスプレイでアプリを開いたときに通知を受け取れます。
カメラセンサーのイメージ バッファの向きとスケーリングは、 ビューファインダー UI 要素の向きとアスペクト比 画面の向きを動的に変える(デバイスの変更の有無にかかわらず) 方向です。
新しいフォーム ファクタ、またはマルチウィンドウまたはマルチディスプレイ環境で、 アプリは、カメラ プレビューの向きがデバイスと同じであると想定する (縦向きまたは横向き)で、プレビューの向きが正しくない、拡大縮小される場合があります 間違っているかもしれません。

図 5 では、デバイスはデバイスを 90° 回転させたと誤って想定されていました。 度(反時計回り)そのため、アプリはプレビューを同じ量だけ回転しました。

図 6 のアプリでは、カメラ プレビュー UI 要素の新しいサイズに合わせて適切にスケーリングできるように、画像バッファのアスペクト比が調整されていません。
向きが固定されているカメラアプリでは、折りたたみ式デバイスで問題が発生することが一般的です。 ノートパソコンなど、その他の大画面デバイス:

図 7 では、アプリの向きが縦向きに限定されているため、カメラアプリの UI が横向きになっています。ビューファインダー画像の向きが正しい カメラセンサーに対して相対的に調整できます。
ポートレート モードをインセット
マルチ ウィンドウ モードをサポートしていないカメラアプリ
(resizeableActivity="false"
)
画面の向きを制限して
(screenOrientation="portrait"
)
または screenOrientation="landscape"
)
大画面のデバイスで縦向きモードをインセットにして配置することで、
カメラ プレビュー。
ディスプレイのアスペクト比が横向きでも、縦向き専用アプリを縦向きでレターボックス表示(インセット)します。横向きのみのアプリは、横向きではレターボックス表示されますが、 ディスプレイのアスペクト比が縦向きです。カメラの画像が回転して配置されます アプリの UI が表示され、カメラ プレビューのアスペクト比に合わせて切り抜かれている 拡大縮小されます。
ポートレート モードのインセットは、カメラ画像のアスペクト比が一定になったときにトリガーされます アプリのプライマリ アクティビティのアスペクト比が一致しません。

図 8 では、縦向き専用のカメラアプリが回転され、ラップトップのディスプレイに UI が縦向きで表示されています。縦向きのアプリと横向きのディスプレイのアスペクト比が異なるため、アプリがレターボックス表示になっています。カメラ プレビュー画像は、アプリの UI の回転(インセット縦向きモードによる)を補正するために回転されています。また、画像は縦向きに収まるように切り抜かれ、スケーリングされているため、画角が狭くなっています。
回転、切り抜き、サイズ変更
横向きのアスペクト比のディスプレイで、縦向き専用のカメラアプリに対して、インセット ポートレート モードが呼び出されます。

アプリが縦向きでレターボックス表示されます。

アプリの向きが変更されたため、カメラ画像が 90 度回転します。

画像はカメラ プレビューのアスペクト比に合わせて切り抜かれた後、次のサイズに拡大されます。 プレビュー全体に表示されます(画角が小さくなります)。

折りたたみ式デバイスでは、カメラ センサーの向きは縦向きでも、ディスプレイのアスペクト比は横向きでもかまいません。

カメラ プレビューはセンサーの向きに合わせて回転するため、ビューファインダーでは画像が正しい向きで表示されますが、縦向き限定のアプリは横向きになります。
インセットの縦向きモードでは、アプリとカメラ プレビューの向きを正しくするために、縦向きでアプリをレターボックス表示するだけで済みます。

API
Android 12(API レベル 31)以降では、アプリは CaptureRequest
クラスの SCALER_ROTATE_AND_CROP
プロパティを使用して、インセット縦向きモードを明示的に制御することもできます。
デフォルト値は SCALER_ROTATE_AND_CROP_AUTO
で、システムが切り欠き縦向きモードを呼び出すことができます。SCALER_ROTATE_AND_CROP_90
は、上記の説明のとおり、インセット縦向きモードの動作です。
すべてのデバイスがすべての SCALER_ROTATE_AND_CROP
値をサポートしているわけではありません。リストを取得するには
値、参照先
CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES
。
CameraX
Jetpack CameraX ライブラリ センサーの向きに合わせてカメラのビューファインダーを作成し デバイスの回転は簡単です。
PreviewView
レイアウト要素はカメラ プレビューを作成し、センサーの向き、デバイスの回転、拡大縮小を自動的に調整します。PreviewView
は、FILL_CENTER
スケールタイプを適用してカメラ画像のアスペクト比を維持します。このスケールタイプでは、画像が中央に配置されますが、PreviewView
のサイズに合わせて切り抜かれることがあります。カメラ画像をレターボックス表示するには、スケールタイプを FIT_CENTER
に設定します。
PreviewView
でカメラ プレビューを作成する基本については、以下をご覧ください。
プレビューを実装する。
完全なサンプル実装については、GitHub の CameraXBasic
リポジトリをご覧ください。
CameraViewfinder
プレビューのユースケースと同様に、CameraViewfinder ライブラリには、カメラ プレビューの作成を簡素化する一連のツールが用意されています。CameraX Core に依存しないため、お使いの Google Cloud 環境にシームレスに統合できます。 既存の Camera2 コードベースを使用します。
Surface
を直接使用する代わりに、CameraViewfinder
ウィジェットを使用して Camera2 のカメラフィードを表示できます。
CameraViewfinder
は内部で TextureView
または SurfaceView
を使用してカメラフィードを表示し、必要な変換を適用してビューファインダーを正しく表示します。これには、アスペクト比、スケール、回転の修正が含まれます。
CameraViewfinder
オブジェクトからサーフェスをリクエストするには、次のようにします。
ViewfinderSurfaceRequest
を作成します。
このリクエストには、CameraCharacteristics
のサーフェス解像度とカメラ デバイス情報に関する要件が含まれています。
requestSurfaceAsync()
を呼び出すと、リクエストがサーフェス プロバイダ(TextureView
または SurfaceView
)に送信され、Surface
の ListenableFuture
が取得されます。
markSurfaceSafeToRelease()
を呼び出しています
サーフェスが不要であることを、サーフェス プロバイダに通知します。
リソースを解放できます
Kotlin
fun startCamera(){ val previewResolution = Size(width, height) val viewfinderSurfaceRequest = ViewfinderSurfaceRequest(previewResolution, characteristics) val surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest) Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> { override fun onSuccess(surface: Surface) { /* create a CaptureSession using this surface as usual */ } override fun onFailure(t: Throwable) { /* something went wrong */} }, ContextCompat.getMainExecutor(context)) }
Java
void startCamera(){ Size previewResolution = new Size(width, height); ViewfinderSurfaceRequest viewfinderSurfaceRequest = new ViewfinderSurfaceRequest(previewResolution, characteristics); ListenableFuture<Surface> surfaceListenableFuture = cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest); Futures.addCallback(surfaceListenableFuture, new FutureCallback<Surface>() { @Override public void onSuccess(Surface result) { /* create a CaptureSession using this surface as usual */ } @Override public void onFailure(Throwable t) { /* something went wrong */} }, ContextCompat.getMainExecutor(context)); }
SurfaceView
SurfaceView
は、プレビューの処理が不要でアニメーション化されていない場合に、カメラ プレビューを作成する簡単な方法です。
SurfaceView
は、画像に合わせてカメラセンサーのイメージ バッファを自動的に回転させます。
センサーの向きとデバイスの両方を考慮したディスプレイの向き
作成できます。ただし、画像バッファは、アスペクト比を考慮せずに SurfaceView
のサイズに合わせてスケーリングされます。
イメージ バッファのアスペクト比とアスペクト比を一致させる必要があります。
SurfaceView
の比率。これはコンテンツをスケーリングすることで実現できます。
コンポーネントの SurfaceView
onMeasure()
メソッド:
(computeRelativeRotation()
のソースコードは、以下の相対回転にあります)。
Kotlin
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val width = MeasureSpec.getSize(widthMeasureSpec) val height = MeasureSpec.getSize(heightMeasureSpec) val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees) if (previewWidth > 0f && previewHeight > 0f) { /* Scale factor required to scale the preview to its original size on the x-axis. */ val scaleX = if (relativeRotation % 180 == 0) { width.toFloat() / previewWidth } else { width.toFloat() / previewHeight } /* Scale factor required to scale the preview to its original size on the y-axis. */ val scaleY = if (relativeRotation % 180 == 0) { height.toFloat() / previewHeight } else { height.toFloat() / previewWidth } /* Scale factor required to fit the preview to the SurfaceView size. */ val finalScale = min(scaleX, scaleY) setScaleX(1 / scaleX * finalScale) setScaleY(1 / scaleY * finalScale) } setMeasuredDimension(width, height) }
Java
@Override void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees); if (previewWidth > 0f && previewHeight > 0f) { /* Scale factor required to scale the preview to its original size on the x-axis. */ float scaleX = (relativeRotation % 180 == 0) ? (float) width / previewWidth : (float) width / previewHeight; /* Scale factor required to scale the preview to its original size on the y-axis. */ float scaleY = (relativeRotation % 180 == 0) ? (float) height / previewHeight : (float) height / previewWidth; /* Scale factor required to fit the preview to the SurfaceView size. */ float finalScale = Math.min(scaleX, scaleY); setScaleX(1 / scaleX * finalScale); setScaleY(1 / scaleY * finalScale); } setMeasuredDimension(width, height); }
SurfaceView
をカメラ プレビューとして実装する方法について詳しくは、以下をご覧ください。
カメラの向き。
TextureView
TextureView
は SurfaceView
よりもパフォーマンスが低く、作業量も増えますが、カメラ プレビューを最大限に制御できます。
TextureView
は、センサーの向きに基づいてセンサー画像バッファを回転しますが、デバイスの回転やプレビューのスケーリングは処理しません。
スケーリングと回転は、行列変換でエンコードできます。TextureView
を正しく拡大縮小して回転する方法については、カメラアプリでサイズ変更可能なサーフェスをサポートするをご覧ください。
相対回転
カメラセンサーの相対回転は、カメラセンサーの出力をデバイスの向きに合わせるために必要な回転量です。
相対回転は SurfaceView
や TextureView
などのコンポーネントで使用される
プレビュー画像の x と y のスケーリング ファクタを決定します。また、kubectl の「get」コマンドや
センサー イメージ バッファの回転を指定します。
「
CameraCharacteristics
および
Surface
クラスを使用すると、
カメラセンサーの相対回転:
Kotlin
/** * Computes rotation required to transform the camera sensor output orientation to the * device's current orientation in degrees. * * @param characteristics The CameraCharacteristics to query for the sensor orientation. * @param surfaceRotationDegrees The current device orientation as a Surface constant. * @return Relative rotation of the camera sensor output. */ public fun computeRelativeRotation( characteristics: CameraCharacteristics, surfaceRotationDegrees: Int ): Int { val sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!! // Reverse device orientation for back-facing cameras. val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT ) 1 else -1 // Calculate desired orientation relative to camera orientation to make // the image upright relative to the device orientation. return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360 }
Java
/** * Computes rotation required to transform the camera sensor output orientation to the * device's current orientation in degrees. * * @param characteristics The CameraCharacteristics to query for the sensor orientation. * @param surfaceRotationDegrees The current device orientation as a Surface constant. * @return Relative rotation of the camera sensor output. */ public int computeRelativeRotation( CameraCharacteristics characteristics, int surfaceRotationDegrees ){ Integer sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); // Reverse device orientation for back-facing cameras. int sign = characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT ? 1 : -1; // Calculate desired orientation relative to camera orientation to make // the image upright relative to the device orientation. return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360; }
ウィンドウ指標
カメラのビューファインダーのサイズを決定する際に画面サイズを使用しないでください。カメラアプリは、モバイル デバイスのマルチウィンドウ モードまたは ChromeOS のフリーフォーム モードで、画面の一部で実行されている場合があります。
WindowManager#getCurrentWindowMetrics()
(API レベル 30 で追加)は、画面のサイズではなく、アプリケーション ウィンドウのサイズを返します。Jetpack WindowManager ライブラリのメソッド
WindowMetricsCalculator#computeCurrentWindowMetrics()
および
WindowInfoTracker#currentWindowMetrics()
API レベル 14 との下位互換性により、同様のサポートが提供されます。
180 度回転
デバイスを 180 度回転しても(たとえば、自然な向きから自然な向きを上下逆にした状態にした場合)、onConfigurationChanged()
コールバックはトリガーされません。その結果、カメラのプレビューが上下逆になることがあります。
180 度回転を検出するには、DisplayListener
を実装し、onDisplayChanged()
コールバックで Display#getRotation()
を呼び出してデバイスの回転を確認します。
限定リソース
Android 10 より前は、マルチウィンドウ環境で最上位に表示されているアクティビティのみが RESUMED
状態になっていました。これはユーザーの混乱を招いていました
どの活動が再開されたかを示すものがありませんでした。
Android 10(API レベル 29)では、表示されているすべてのアクティビティが RESUMED
状態になるマルチ再開が導入されました。表示中のアクティビティは引き続き PAUSED
に入ることができます
アクティビティの上に透明なアクティビティがある場合や、
アクティビティがフォーカス可能でない場合。たとえば、ピクチャー イン ピクチャー モードの場合(
ピクチャー イン ピクチャーのサポート)。
カメラ、マイク、または専用または
API レベル 29 以降のシングルトン リソースは、複数のアプリの再開をサポートする必要があります。たとえば、3 つの再開アクティビティがカメラを使用する場合、この排他的リソースにアクセスできるのは 1 つだけです。各アクティビティでは、
onDisconnected()
より優先度の高いカメラへのプリエンプティブ アクセスを常に認識
できます。
詳細については、次をご覧ください: 複数のアプリの再開。
参考情報
- Camera2 のサンプルについては、GitHub の Camera2Basic アプリをご覧ください。
- CameraX プレビューのユースケースについては、CameraX をご覧ください。 プレビューを実装する。
- CameraX カメラ プレビューのサンプル実装については、GitHub の CameraXBasic リポジトリをご覧ください。
- ChromeOS のカメラ プレビューについては、カメラの向きをご覧ください。
- 折りたたみ式デバイス向けの開発については、折りたたみ式デバイスの詳細をご覧ください。