В этом уроке мы обсудим, как напрямую управлять аппаратным обеспечением камеры, используя API фреймворка.
Примечание: На этой странице используется устаревший класс Camera . Мы рекомендуем использовать CameraX или, для определенных случаев, Camera2 . Оба класса, CameraX и Camera2, поддерживают Android 5.0 (уровень API 21) и выше.
Для непосредственного управления камерой устройства требуется гораздо больше кода, чем для запроса фотографий или видео из существующих приложений для работы с камерой. Однако, если вы хотите создать специализированное приложение для работы с камерой или что-то полностью интегрированное в пользовательский интерфейс вашего приложения, этот урок покажет вам, как это сделать.
Обратитесь к следующим соответствующим ресурсам:
Откройте объект «Камера».
Получение экземпляра объекта Camera — это первый шаг в процессе непосредственного управления камерой. Как и в собственном приложении камеры Android, рекомендуемый способ доступа к камере — это открытие Camera в отдельном потоке, запускаемом из onCreate() . Такой подход хорош, поскольку он может занять некоторое время и замедлить поток пользовательского интерфейса. В более простой реализации открытие камеры можно отложить до метода onResume() , чтобы упростить повторное использование кода и сохранить понятный поток управления.
Вызов метода Camera.open() вызывает исключение, если камера уже используется другим приложением, поэтому мы оборачиваем его в блок try .
Котлин
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; } }
Начиная с API уровня 9, платформа для работы с камерами поддерживает несколько камер. Если вы используете устаревший API и вызываете метод open() без аргументов, вы получаете первую камеру, направленную назад.
Создать предварительный просмотр камеры
Для того чтобы сделать снимок, обычно пользователям необходимо увидеть предварительный просмотр объекта съемки перед нажатием кнопки спуска затвора. Для этого можно использовать SurfaceView для отображения предварительного просмотра того, что фиксирует датчик камеры.
Предварительный просмотр занятия
Для начала работы с отображением предварительного просмотра вам потребуется класс preview. Предварительный просмотр требует реализации интерфейса android.view.SurfaceHolder.Callback , который используется для передачи данных изображения с аппаратного обеспечения камеры в приложение.
Котлин
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); } ... }
Как показано в следующем разделе, перед началом предварительного просмотра изображения в реальном времени необходимо передать класс предварительного просмотра объекту Camera .
Настройте и запустите предварительный просмотр.
Экземпляр камеры и связанный с ним предварительный просмотр должны создаваться в определенном порядке, причем объект камеры должен быть создан первым. В следующем фрагменте кода процесс инициализации камеры инкапсулирован таким образом, что метод Camera.startPreview() вызывается методом setCamera() всякий раз, когда пользователь изменяет положение камеры. Предварительный просмотр также должен быть перезапущен в методе обратного вызова surfaceChanged() класса предварительного просмотра.
Котлин
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(); } }
Изменить настройки камеры
Настройки камеры изменяют способ съемки, от уровня масштабирования до компенсации экспозиции. В этом примере изменяется только размер предварительного просмотра; для получения информации о многих других параметрах см. исходный код приложения «Камера».
Котлин
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(); }
Установите ориентацию предварительного просмотра.
Большинство приложений камеры блокируют отображение в альбомном режиме, поскольку это естественная ориентация датчика камеры. Эта настройка не мешает делать фотографии в портретном режиме, так как ориентация устройства записывается в заголовок EXIF. Метод setCameraDisplayOrientation() позволяет изменить способ отображения предварительного просмотра, не влияя на способ записи изображения. Однако в Android до API уровня 14 необходимо остановить предварительный просмотр перед изменением ориентации, а затем перезапустить его.
Сфотографируйте
Используйте метод Camera.takePicture() для съемки снимка после начала предварительного просмотра. Вы можете создать объекты Camera.PictureCallback и Camera.ShutterCallback и передать их в Camera.takePicture() .
Если вам нужно непрерывно захватывать изображения, вы можете создать Camera.PreviewCallback , реализующий интерфейс onPreviewFrame() . Для промежуточного варианта вы можете захватывать только выбранные кадры предварительного просмотра или настроить отложенное действие для вызова takePicture() .
Перезапустите предварительный просмотр
После того, как снимок сделан, необходимо перезапустить предварительный просмотр, прежде чем пользователь сможет сделать следующий снимок. В этом примере перезапуск осуществляется путем перегрузки кнопки спуска затвора.
Котлин
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(); }
Остановите предварительный просмотр и отпустите камеру.
После завершения работы приложения с камерой настало время очистки. В частности, необходимо освободить объект Camera , иначе вы рискуете вызвать сбой в работе других приложений, включая новые экземпляры вашего собственного приложения.
Когда следует остановить предварительный просмотр и отпустить камеру? Разрушение поверхности предварительного просмотра — довольно хороший признак того, что пора остановить предварительный просмотр и отпустить камеру, как показано в этих методах класса Preview .
Котлин
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; } }
Ранее в уроке эта процедура также являлась частью метода setCamera() , поэтому инициализация камеры всегда начинается с остановки предварительного просмотра.