Северный (Арктический) федеральный университет им. М.В. Ломоносова
Опубликован: 10.04.2014 | Доступ: свободный | Студентов: 6839 / 1236 | Длительность: 14:18:00
Специальности: Программист
Самостоятельная работа 7:

Многооконное приложение

12.3 Настройка интерфейса и реализация логики активности для работы с камерой

Настроим интерфейс активности для работы с камерой. Нам понадобится окно предварительного просмотра, добавим в окно активности элемент SurfaceView. А так же нам понадобится кнопка для выполнения снимков, добавим в окно активности элемент ImageButton. Далее предлагаем настроить интерфейс самостоятельно. В нашем случае активность выглядит так, как показано на рис. 12.1.

Интерфейс активности для работы с камерой

увеличить изображение
Рис. 12.1. Интерфейс активности для работы с камерой

Реализуем логику активности, в данном случае необходимо при нажатии на кнопку выполнить снимок. Работаем с java файлом, описывающим соответствующий класс активности, CameraActivity.java.

Объявим следующие поля класса активности:

private Camera camera; //для проведения всех операций с камерой
private SurfaceHolder surfaceHolder; //для задания preview
private SurfaceView preview; //для отображения окна предпросмотра
private View shotBtn;  //для выполнения снимка (кнопка)
    

Для начала работы с камерой необходимо ее инициализировать, сделать это лучше в методе onResume() класса активности, для инициализации используется следующая конструкция:

camera = Camera.open();
    

После завершения работы с камерой, необходимо ее освободить для других приложений, сделать это лучше в методе onPause() класса активности, для освобождения камеры используется следующая конструкция:

Camera.release();
    

Для активности, работающей с камерой, имеет смысл сразу задать расположение экрана следующим образом

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    

в противном случае, приходится, например, в методе surfaceCreated() проверять расположение экрана и поворачивать окно предварительного просмотра, что не очень удобно, так как поворот экрана занимает некоторое время.

Выполним настройку окна предварительного просмотра:

preview = (SurfaceView) findViewById(R.id.surfaceCamera);
surfaceHolder = preview.getHolder();
surfaceHolder.addCallback(new MyCallback());
    

метод addCallback() используется для отслеживания изменений окна предпросмотра. Параметром этого метода служит экземпляр класса реализующего интерфейс SurfaceHolder.Callback, в нашем случае для реализации этого интерфейса создан внутренний класс активности MyCallback.

В этом классе необходимо реализовать методы:

public void surfaceCreated(SurfaceHolder holder);
public void surfaceChanged(SurfaceHolder holder, int format, 
int width, int height);
public void surfaceDestroyed(SurfaceHolder holder);
    

Мы реализуем метод surfaceCreated(), оставшиеся два метода запишем с пустой реализацией.

В методе surfaceCreated() зададим окно предварительного просмотра:

camera.setPreviewDisplay(holder);
    

Выполним необходимые настройки окна предпросмотра (см. Листинг 12.1).

Запустим отображение окна предварительного просмотра:

camera.startPreview();
    

Пришло время обсудить и реализовать выполнение фотосъемки. Для того, чтобы сделать снимок необходимо вызвать метод takePicture(). Вызов этого метода можно выполнить из метода-обработчика события нажатия кнопки, тогда при нажатии на кнопку сразу будет получена фотография, а можно "растянуть удовольствие" и воспользоваться предварительной автофокусировкой.

Сначала инициализируем кнопку и добавим слушателя события нажатия в методе onCreate() активности:

shotBtn = findViewById(R.id.bCameraShot);
shotBtn.setOnClickListener(new MyViewListener());
    

В данном случае слушателем является экземпляр внутреннего класса MyViewListener, который реализует интерфейс View.OnClickListener.

class MyViewListener implements View.OnClickListener{
    @Override
    public void onClick(View v){
        if (v == shotBtn){
            camera.autoFocus(new MyAutoFocusCallback());
        }
    }
}
    

В методе onClick() вызываем обработчик автофокуса, слушателем события автофокусировки в данном случае является экземпляр внутреннего класса MyAutoFocusCallback, который реализует интерфейс Camera.AutoFocusCallback.

class MyAutoFocusCallback implements Camera.AutoFocusCallback{
    @Override
    public void onAutoFocus(boolean paramBoolean, Camera 
                                             paramCamera){
    if (paramBoolean){
        // если удалось сфокусироваться, делаем снимок
        paramCamera.takePicture(null, null, null, 
                                      new MyPictureCallback());
        } 
    }
} 
    

Метод takePicture() имеет четыре параметра:

Camera.ShutterCallback shutter - вызывается в момент получения изображения с матрицы;
Camera.PictureCallback raw - программе передаются для обработки raw данные (если поддерживается аппаратно);
Camera.PictureCallback postview - программе передаются полностью обработанные данные (если поддерживается аппаратно);
Camera.PictureCallback jpg - программе передается изображение в формате jpg. Здесь может быть организована запись изображения на карту памяти.

В нашем случае последним параметром является экземпляр класса MyPictureCallback(), который реализует интерфейс Camera.PictureCallback. В этом классе реализован единственный метод (см. Листинг 12.1):

public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera){...}
    

В листинге 12.1 представлен код активности, реализующей работу с камерой и позволяющей выполнять снимки.

Чтобы разрешить приложению работать с камерой и сохранять фотографии на карте памяти, в манифест необходимо добавить следующие разрешения:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
Марат Нуриджанян
Марат Нуриджанян

Пример: Скачать среду можно с сайта для разработчиков Android (http://developer.android.com/sdk/index.html).

Там скачать можно только Android Studio

Владимир Каункин
Владимир Каункин

В самостоятельной работе 2 в примере решения задания некорректно загадывается число (в двух местах), выбирая случайное целое число из диапазона [0, 99] вместо [1, 100], как того требует условие. Кроме того, загадывание числа всё таки лучше вынести в отдельный метод, как мне кажется.