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

В этом уроке мы рассмотрим, как управлять самой камерой непосредственно с помощью API платформы.

Непосредственное управление камерой устройства требует гораздо больше кода, чем запрос фотографии или видео из существующих приложений камеры. Тем не менее, если вы хотите построить специализированное приложение камеры, или что-то полностью интегрированное в ваш пользовательский интерфейс приложения, этот урок покажет вам, как это сделать.

Создание объекта камеры

Получение экземпляра Camera объекта является первым шагом в процессе непосредственного управления камерой. Так же как делает собственное приложение камеры Android, рекомендуемый способ получения доступа к камере это вызвать open() метод объекта Camera в отдельном потоке, который запускается из onCreate(). Этот подход является хорошей идеей, так как это действие может занять некоторое время, и, возможно, заставит зависнуть поток пользовательского интерфейса. В более простой реализации, открытие камеры можно отложить до вызова onResume() методы для облегчения повторного использования кода, и сохранения процесса управления простым.

Вызов Camera.open() генерирует исключение, если камера уже используется другим приложением, поэтому мы оберните его в try блок.

private boolean safeCameraOpen(int id) {
    boolean qOpened = false;
  
    try {
        releaseCameraAndPreview();
        mCamera = Camera.open(id);
        qOpened = (mCamera != null);
    } catch (Exception e) {
        Log.e(getString(R.string.app_name), "failed to open Camera");
        e.printStackTrace();
    }

    return qOpened;    
}

private void releaseCameraAndPreview() {
    mPreview.setCamera(null);
    if (mCamera != null) {
        mCamera.release();
        mCamera = null;
    }
}

Начиная с уровня API 9, библиотека для работы с камерой поддерживает несколько камер. Если вы используете устаревший API и вызовите open() без аргумента, вы получите первую камеру с задней стороны устройства.

Создание предварительного просмотра

Съемка обычно требует, чтобы пользователи видели предварительный просмотр своего предмета, прежде чем нажать кнопку спуска затвора. Чтобы сделать это, вы можете использовать SurfaceView для отрисовки предварительного просмотра того, что видит датчик камеры.

Класс для предварительного просмотра

Чтобы начать показ предварительного просмотра, вам нужен класс предварительного просмотра. Предварительный просмотр требует реализацию android.view.SurfaceHolder.Callback интерфейса, который используется для передачи данных изображения от устройства камеры в приложение.

class Preview extends ViewGroup implements SurfaceHolder.Callback {

    SurfaceView mSurfaceView;
    SurfaceHolder mHolder;

    Preview(Context context) {
        super(context);

        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
...
}

Класс предварительного просмотра должен быть передан в объект Camera до того, как предварительный просмотр изображений может быть запущен, как показано в следующем разделе.

Задать и запустить предварительный просмотр

Экземпляр камеры и связанный с ним предварительный просмотр должен быть создан в определенном порядке, при чем объект камеры должен быть первым. В приведенном ниже фрагменте, процесс инициализации камеры инкапсулируется так, что Camera.startPreview() вызывается setCamera() методом, когда пользователь делает что-либо для изменения камеры. Предварительный просмотр также должен быть перезапущен в классе предварительного просмотра в surfaceChanged() методе обратного вызова.

public void setCamera(Camera camera) {
    if (mCamera == camera) { return; }
    
    stopPreviewAndFreeCamera();
    
    mCamera = camera;
    
    if (mCamera != null) {
        List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
        mSupportedPreviewSizes = localSizes;
        requestLayout();
      
        try {
            mCamera.setPreviewDisplay(mHolder);
        } 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();
    }
}

Изменение настроек камеры

Настройки камеры изменяют способ, которым камера делает снимки, от уровня масштабирования до коррекции кадров. В этом примере изменяется только размер предварительного просмотра; см. исходный код приложения Камера для более детального изучения.

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(mPreviewSize.width, mPreviewSize.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().

Перезапуск предварительного просмотра

После съемки кадра, необходимо перезапустить предварительный просмотр перед тем, как пользователь сможет принять другую картину. В этом примере, перезапуск осуществляется путем перегрузки обработчика кнопки спуска затвора.

@Override
public void onClick(View v) {
    switch(mPreviewState) {
    case K_STATE_FROZEN:
        mCamera.startPreview();
        mPreviewState = K_STATE_PREVIEW;
        break;

    default:
        mCamera.takePicture( null, rawCallback, null);
        mPreviewState = K_STATE_BUSY;
    } // switch
    shutterBtnConfig();
}

Остановка предварительного просмотра и освобождение камеры

Как только ваше приложение выполнило требуемое действие с помощью камеры, необходимо очистить и освободить объекты. В частности, необходимо освободить объект Camera , иначе вы рискуете сломать другие приложения, в том числе создание новых экземпляров в вашем собственном приложении.

Когда нужно остановить предварительный просмотр и освободить камеру? Что же, когда окно вашего предварительного просмотра уничтожается это является очень хорошим намеком, что пора остановить просмотр и освободить камеру, как показано в следующих методах Preview класса.

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