Статьи

Основы Android: создание зеркала

Узнайте, как сделать простое «зеркальное» приложение в этом быстром уроке!

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

Начиная с уровня API 9, Android SDK предоставляет метод для доступа к нескольким камерам на устройствах. И с тех пор большинство производителей включили фронтальную камеру. Эта камера обычно используется для съемки автопортретов или для видеочата.


Открытый исходный код для этого проекта можно загрузить и использовать локально или просматривать онлайн. Кроме того, вы можете создать свой собственный проект. Это отдельный проект, поэтому вы также можете добавить это действие в свой проект. Предположим, у вас есть готовый проект для работы.


Типичный способ представления камеры — создание макета — любого, который вы пожелаете — с элементом управления FrameLayout для предварительного просмотра камеры. Обычно там тоже есть какая-то кнопка для съемки. Имея это в виду, вот макет, который мы использовали для портретного режима:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
<?xml version=»1.0″ encoding=»utf-8″?>
<RelativeLayout xmlns:android=»http://schemas.android.com/apk/res/android»
    android:layout_width=»match_parent»
    android:layout_height=»match_parent» >
    <FrameLayout
        android:id=»@+id/camPreview»
        android:layout_width=»wrap_content»
        android:layout_height=»wrap_content»
        android:layout_centerHorizontal=»true»
        android:layout_centerVertical=»true» >
    </FrameLayout>
    <Button
        android:id=»@+id/capture»
        android:layout_width=»wrap_content»
        android:layout_height=»wrap_content»
        android:layout_alignParentBottom=»true»
        android:layout_centerHorizontal=»true»
        android:text=»@string/capture» />
</RelativeLayout>

Ландшафтная компоновка аналогична, но кнопка расположена вдоль стороны.


Доступ к камерам устройства осуществляется с помощью класса Camera (android.hardware.Camera) из Android SDK. Разумное место для настройки и запуска камеры — метод onCreate () вашего класса Activity, например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
private Camera mCam;
private MirrorView mCamPreview;
private int mCameraId = 0;
private FrameLayout mPreviewLayout;
 
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
mCameraId = findFirstFrontFacingCamera();
 
    mPreviewLayout = (FrameLayout) findViewById(R.id.camPreview);
    mPreviewLayout.removeAllViews();
 
    startCameraInLayout(mPreviewLayout, mCameraId);
 
}

Сначала мы определяем, какая камера является фронтальной камерой. Поскольку в API предусмотрена возможность использования более одной фронтальной камеры, мы используем первую. Затем FrameLayout загружается и очищается. Нам нужен только предварительный просмотр камеры. Затем, наконец, мы запускаем камеру в этом макете.

Теперь давайте посмотрим на каждый из этих вспомогательных методов.


Используйте метод getNumberOfCameras () объекта Camera, чтобы выполнить итерацию по каждому экземпляру камеры и извлечь объект CameraInfo. Используйте лицевое поле, чтобы определить, является ли камера CAMERA_FACING_FRONT. Если это так, верните этот идентификатор камеры — мы нашли фронтальную камеру для использования.

01
02
03
04
05
06
07
08
09
10
11
12
13
private int findFirstFrontFacingCamera() {
    int foundId = -1;
    int numCams = Camera.getNumberOfCameras();
    for (int camId = 0; camId < numCams; camId++) {
        CameraInfo info = new CameraInfo();
        Camera.getCameraInfo(camId, info);
        if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
            foundId = camId;
            break;
        }
    }
    return foundId;
}

Затем получите экземпляр объекта Camera с помощью вызова метода Camera.open (), используя идентификатор камеры для фронтальной камеры. Создание объекта MirrorView с использованием открытой камеры и добавление объекта MirrorView в макет.

1
2
3
4
5
6
7
private void startCameraInLayout(FrameLayout layout, int cameraId) {
    mCam = Camera.open(cameraId);
    if (mCam != null) {
        mCamPreview = new MirrorView(this, mCam);
        layout.addView(mCamPreview);
    }
}

Теперь давайте подробнее рассмотрим класс MirrorView.


Чтобы разместить предварительный просмотр на экране для отображения того, что захватывает камера, мы должны создать новый SurfaceView, чтобы мы могли назначить его держатель (SurfaceHolder) для аппаратного обеспечения камеры. Это позволяет аппаратным средствам камеры напрямую записывать на поверхность.

Вот реализация нашего MirrorView:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class MirrorView extends SurfaceView implements
        SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
 
    public MirrorView(Context context, Camera camera) {
        super(context);
        mCamera = camera;
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
 
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (Exception error) {
            Log.d(DEBUG_TAG,
                    «Error starting mPreviewLayout: » + error.getMessage());
        }
    }
 
    public void surfaceDestroyed(SurfaceHolder holder) {
    }
 
    public void surfaceChanged(SurfaceHolder holder, int format, int w,
            int h) {
        if (mHolder.getSurface() == null) {
            return;
        }
 
        // can’t make changes while mPreviewLayout is active
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
 
        }
 
        try {
 
            // start up the mPreviewLayout
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
 
        } catch (Exception error) {
            Log.d(DEBUG_TAG,
                    «Error starting mPreviewLayout: » + error.getMessage());
        }
    }
}

Когда поверхность создана, в вызове onSurfaceCreated () устанавливается камера SurfaceHolder, и предварительный просмотр начинается с вызова метода startPreview (). Когда поверхность изменяется, при вызове onSurfaceChanged () камера останавливается и перезапускается с обновленным держателем. Для полной реализации этого решения, с объявлениями для DEBUG_TAG и так далее, см. Полный исходный код.

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


Ориентация предварительного просмотра камеры должна соответствовать ориентации экрана устройства, чтобы все отображалось правильно. Кроме того, соотношение сторон предварительного просмотра камеры должно соответствовать размеру предварительного просмотра, в противном случае изображение будет выглядеть искаженным. Все эти данные легко доступны из классов Display, CameraInfo и Camera.Parameters. Здесь мы написали простой метод, который регулирует характеристики отображения SurfaceHolder для решения этих проблем:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void setCameraDisplayOrientationAndSize() {
    CameraInfo info = new CameraInfo();
    Camera.getCameraInfo(mCameraId, info);
    int rotation = getWindowManager().getDefaultDisplay().getRotation();
    int degrees = rotation * 90;
 
    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        result = (info.orientation + degrees) % 360;
        result = (360 — result) % 360;
    } else {
        result = (info.orientation — degrees + 360) % 360;
    }
    mCamera.setDisplayOrientation(result);
 
    Camera.Size previewSize = mCam.getParameters().getPreviewSize();
    if (result == 90 || result == 270) {
        mHolder.setFixedSize(previewSize.height, previewSize.width);
    } else {
        mHolder.setFixedSize(previewSize.width, previewSize.height);
 
    }
}

На данный момент зеркало должно быть полностью функциональным.

Зеркало в действии

Объект Camera должен быть освобожден, как только ваше приложение будет готово к нему. В случае примера Activity мы можем освободить камеру в методе onPause () и запустить ее в onResume (), например так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
@Override
protected void onResume() {
    super.onResume();
    if (mCam == null && mPreviewLayout != null) {
        mPreviewLayout.removeAllViews();
        startCameraInLayout(mPreviewLayout, mCameraId);
    }
}
 
@Override
protected void onPause() {
    if (mCam != null) {
        mCam.release();
        mCam = null;
    }
    super.onPause();
}

Никогда не стоит недооценивать, насколько пользователи любят свои фронтальные камеры. Многие из смартфонов последнего поколения имеют эту функцию, но лишь немногие приложения используют ее — ваши должны! Вы узнали, как идентифицировать и использовать фронтальные камеры в этом уроке. Кроме того, вы узнали, как настроить ориентацию экрана и соотношение сторон в разделе предварительного просмотра.

Можете ли вы сохранить изображение сейчас? (Подсказка: вы загрузили открытый исходный код, верно?)


Разработчики мобильных приложений Лорен Дарси и Шейн Кондер являются соавторами нескольких книг по разработке Android: углубленная книга по программированию под названием « Разработка беспроводных приложений для Android» и « Самс научи себя разрабатывать приложения для Android за 24 часа» . Когда они не пишут, они тратят свое время на разработку мобильного программного обеспечения в своей компании и оказание консультационных услуг. С ними можно связаться по электронной почте [email protected] , через их блог на androidbook.blogspot.com и в Twitter @androidwireless .