Управление камерой

В этом уроке мы обсудим, как управлять оборудованием камеры напрямую с помощью 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
    }
}

Ява

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 для предварительного просмотра того, что фиксирует датчик камеры.

Предварительный просмотр класса

Чтобы начать отображать предварительный просмотр, вам понадобится класс предварительного просмотра. Для предварительной версии требуется реализация интерфейса 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)
    }
    ...
}

Ява

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()
    }
}

Ява

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()
    }
}

Ява

@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()
}

Ява

@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
    }
}

Ява

@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() , поэтому инициализация камеры всегда начинается с остановки предварительного просмотра.