Au cours de cette leçon, nous allons voir comment contrôler la partie matérielle d'un appareil photo directement à l'aide des API du framework.
Remarque : La classe Camera, qui est mentionnée sur cette page, est obsolète. Nous vous recommandons d'utiliser CameraX ou, dans des cas d'utilisation spécifiques, Camera2. CameraX et Camera2 sont compatibles avec Android 5.0 (niveau d'API 21) ou version ultérieure.
Contrôler directement l'appareil photo d'un appareil nécessite beaucoup plus de codes que de demander des photos ou vidéos aux applications dédiées existantes. Toutefois, si vous voulez créer une application Appareil photo spécialisée ou une solution entièrement intégrée dans l'interface utilisateur de votre application, cette leçon vous montre comment procéder.
Consultez les ressources associées suivantes :
Ouvrir l'objet Camera
La première étape pour contrôler directement l'appareil photo consiste à obtenir une instance
de l'objet Camera
. Comme le fait la propre application Appareil photo d'Android,
nous vous recommandons d'accéder à l'appareil photo en ouvrant Camera
sur un thread distinct,
lancé à partir de onCreate()
. Cette approche est judicieuse,
car la procédure peut prendre un certain temps et bloquer le thread UI. Dans une implémentation plus basique,
l'ouverture de l'objet Camera peut être reportée à la méthode onResume()
pour faciliter la réutilisation du code et simplifier le
flux de contrôle.
L'appel de Camera.open()
génère une
exception si l'appareil photo est déjà utilisé par une autre application. C'est pourquoi nous l'encapsulons
dans un bloc try
.
Kotlin
private fun safeCameraOpen(id: Int): Boolean { return try { releaseCameraAndPreview() mCamera = Camera.open(id) true } catch (e: Exception) { Log.e(getString(R.string.app_name), "failed to open Camera") e.printStackTrace() false } } private fun releaseCameraAndPreview() { preview?.setCamera(null) mCamera?.also { camera -> camera.release() mCamera = null } }
Java
private boolean safeCameraOpen(int id) { boolean qOpened = false; try { releaseCameraAndPreview(); camera = Camera.open(id); qOpened = (camera != null); } catch (Exception e) { Log.e(getString(R.string.app_name), "failed to open Camera"); e.printStackTrace(); } return qOpened; } private void releaseCameraAndPreview() { preview.setCamera(null); if (camera != null) { camera.release(); camera = null; } }
Depuis le niveau d'API 9, le framework d'appareil photo est compatible avec plusieurs caméras. Si vous utilisez
l'ancienne API et appelez open()
sans argument,
vous obtenez la première caméra arrière.
Créer l'aperçu de l'appareil photo
Pour prendre une photo, l'utilisateur doit généralement prévisualiser le sujet avant d'appuyer
sur l'obturateur. Pour cela, vous pouvez utiliser un SurfaceView
pour afficher un aperçu de ce que le capteur photo détecte.
Classe Preview
Pour afficher un aperçu, vous avez d'abord besoin d'une classe Preview. L'aperçu
nécessite d'implémenter l'interface android.view.SurfaceHolder.Callback
, qui permet de transmettre à l'application les données d'image
provenant de la partie matérielle de l'appareil photo.
Kotlin
class Preview( context: Context, val surfaceView: SurfaceView = SurfaceView(context) ) : ViewGroup(context), SurfaceHolder.Callback { var mHolder: SurfaceHolder = surfaceView.holder.apply { addCallback(this@Preview) setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } ... }
Java
class Preview extends ViewGroup implements SurfaceHolder.Callback { SurfaceView surfaceView; SurfaceHolder holder; Preview(Context context) { super(context); surfaceView = new SurfaceView(context); addView(surfaceView); // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. holder = surfaceView.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } ... }
Comme indiqué dans la prochaine section, la classe Preview doit être transmise à l'objet Camera
avant que l'aperçu de l'image en direct puisse être lancé.
Configurer et lancer l'aperçu
Vous devez créer une instance d'appareil photo et l'aperçu associé dans un ordre précis,
en plaçant l'objet Camera en premier. Dans l'extrait ci-dessous,
le processus d'initialisation de l'appareil photo est encapsulé de sorte que Camera.startPreview()
est appelé par
la méthode setCamera()
chaque fois que l'utilisateur apporte une modification
à l'appareil photo. L'aperçu doit également être relancé dans la méthode de rappel surfaceChanged()
de la classe Preview.
Kotlin
fun setCamera(camera: Camera?) { if (mCamera == camera) { return } stopPreviewAndFreeCamera() mCamera = camera mCamera?.apply { mSupportedPreviewSizes = parameters.supportedPreviewSizes requestLayout() try { setPreviewDisplay(holder) } catch (e: IOException) { e.printStackTrace() } // Important: Call startPreview() to start updating the preview // surface. Preview must be started before you can take a picture. startPreview() } }
Java
public void setCamera(Camera camera) { if (mCamera == camera) { return; } stopPreviewAndFreeCamera(); mCamera = camera; if (mCamera != null) { List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes(); supportedPreviewSizes = localSizes; requestLayout(); try { mCamera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); } // Important: Call startPreview() to start updating the preview // surface. Preview must be started before you can take a picture. mCamera.startPreview(); } }
Modifier les paramètres de l'appareil photo
Les paramètres de l'appareil photo changent la façon dont les photos sont prises (cela va du niveau de zoom jusqu'à la correction d'exposition). Dans cet exemple, seule la taille de l'aperçu change. Pour en savoir plus, consultez le code source de l'application Appareil photo.
Kotlin
override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { mCamera?.apply { // Now that the size is known, set up the camera parameters and begin // the preview. parameters?.also { params -> params.setPreviewSize(previewSize.width, previewSize.height) requestLayout() parameters = params } // Important: Call startPreview() to start updating the preview surface. // Preview must be started before you can take a picture. startPreview() } }
Java
@Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Now that the size is known, set up the camera parameters and begin // the preview. Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewSize(previewSize.width, previewSize.height); requestLayout(); mCamera.setParameters(parameters); // Important: Call startPreview() to start updating the preview surface. // Preview must be started before you can take a picture. mCamera.startPreview(); }
Définir l'orientation de l'aperçu
La plupart des applications Appareil photo verrouillent l'affichage en mode Paysage, car c'est l'orientation
naturelle du capteur. Sachez que ce paramètre ne vous empêche pas de prendre des photos en mode Portrait,
car l'orientation de l'appareil est enregistrée dans l'en-tête EXIF. La méthode setCameraDisplayOrientation()
vous permet de modifier
comment l'aperçu est affiché sans affecter la façon dont l'image est enregistrée. Toutefois, avant le niveau d'API 14 sous Android,
vous devez arrêter l'aperçu avant de changer l'orientation, puis le relancer.
Prendre une photo
Pour prendre une photo
une fois l'aperçu lancé, utilisez la méthode Camera.takePicture()
. Vous pouvez créer des objets Camera.PictureCallback
et Camera.ShutterCallback
, puis les transmettre dans Camera.takePicture()
.
Si vous voulez saisir des images en continu, vous pouvez créer un Camera.PreviewCallback
qui implémente onPreviewFrame()
. Pour
quelque chose d'intermédiaire, vous pouvez ne capturer que les frames d'aperçu sélectionnés ou
configurer une action différée pour appeler takePicture()
.
Relancer l'aperçu
Une fois qu'une photo est prise, vous devez relancer l'aperçu avant que l'utilisateur puisse en prendre une autre. Dans cet exemple, l'aperçu est relancé en surchargeant le bouton de l'obturateur.
Kotlin
fun onClick(v: View) { previewState = if (previewState == K_STATE_FROZEN) { camera?.startPreview() K_STATE_PREVIEW } else { camera?.takePicture(null, rawCallback, null) K_STATE_BUSY } shutterBtnConfig() }
Java
@Override public void onClick(View v) { switch(previewState) { case K_STATE_FROZEN: camera.startPreview(); previewState = K_STATE_PREVIEW; break; default: camera.takePicture( null, rawCallback, null); previewState = K_STATE_BUSY; } // switch shutterBtnConfig(); }
Arrêter l'aperçu et libérer l'objet Camera
Une fois que votre application a terminé d'utiliser l'appareil photo, il est temps de "nettoyer". Plus précisément,
vous devez libérer l'objet Camera
sous peine de faire planter d'autres applications,
y compris de nouvelles instances de votre propre application.
Quand devez-vous arrêter l'aperçu et libérer l'objet Camera ? La destruction de
la surface d'aperçu est un signe qu'il est temps d'arrêter l'aperçu et de libérer l'objet Camera,
comme indiqué dans ces méthodes de la classe Preview
.
Kotlin
override fun surfaceDestroyed(holder: SurfaceHolder) { // Surface will be destroyed when we return, so stop the preview. // Call stopPreview() to stop updating the preview surface. mCamera?.stopPreview() } /** * When this function returns, mCamera will be null. */ private fun stopPreviewAndFreeCamera() { mCamera?.apply { // Call stopPreview() to stop updating the preview surface. stopPreview() // Important: Call release() to release the camera for use by other // applications. Applications should release the camera immediately // during onPause() and re-open() it during onResume()). release() mCamera = null } }
Java
@Override public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return, so stop the preview. if (mCamera != null) { // Call stopPreview() to stop updating the preview surface. mCamera.stopPreview(); } } /** * When this function returns, mCamera will be null. */ private void stopPreviewAndFreeCamera() { if (mCamera != null) { // Call stopPreview() to stop updating the preview surface. mCamera.stopPreview(); // Important: Call release() to release the camera for use by other // applications. Applications should release the camera immediately // during onPause() and re-open() it during onResume()). mCamera.release(); mCamera = null; } }
Plutôt tôt dans la leçon, cette procédure faisait également partie de la méthode setCamera()
. C'est pourquoi l'initialisation d'un appareil photo commence toujours
par l'arrêt de l'aperçu.