LXF164: Android
|
|
|
Android
Программирование: Взаимодействие с web-камерой телефона
Содержание |
Делаем снимки
Не нравится программа для камеры в своем телефоне? Есть идея программы для работы с фото? Джульетта Кемп знакомит с Android Camera API.
В подавляющем большинстве телефонов с Android есть встроенные камеры, и с развитием технологий они становятся все удобнее. Программ, которые так или иначе работают с камерой, великое множество, и предоставляемое Android API максимально упрощает эту задачу.
Если вам нужен всего-то один снимок, возможно, проще воспользоваться Намерением [Intent] для активации встроенной камеры (см. врезку внизу), но если вам нужно больше рычагов управления, можно написать собственное Занятие [Activity] Camera.
На нашем уроке мы создадим Занятие, которое просто использует камеру, но, разумеется, ничто не мешает создать приложение с другим главным Занятием, где заодно будет и Занятие для камеры, вызываемое при необходимости.
Подготовка камеры
Прежде чем писать код, следует объявить требования для камеры в AndroidManifest.xml:
<manifest .... >
<uses-permission android:name=”android.permission.CAMERA” />
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />
<uses-feature android:name=”android.hardware.camera” />
Если вы не собираетесь сохранять изображения, WRITE_EXTERNAL_STORAGE не понадобится (но так как мы позже это сделаем, то добавим ее сразу). Директива uses-feature означает, что для установки приложения в телефоне обязательно должна быть камера. Если камера – только часть вашей программы, и пользователи прекрасно смогут работать с программой и без нее, добавьте android:required=“false” к этой строке. Также можно задать характеристики камеры; более подробно они описаны в одном из следующих разделов.
Теперь можно продолжить с кодом первоначальной настройки, в котором мы впервые получаем экземпляр объекта камеры. Метод onCreate() в MyCameraActivity должен выглядеть так:
@Override public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if (!checkCameraExists(this)) {
Toast.makeText(this, “Увы, камеры нет!”, Toast.LENGTH_LONG);
finish();
}
camera = getCameraInstance();
}
Один из параметров манифеста требует обязательного наличия камеры на устройстве, но ее наличие стоит проверить и здесь. Метод checkCameraExists() прост:
private boolean checkCameraExists(Context c) {
if (c.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
return true;
} else {
return false;
}
}
Метод getCameraInstance() ненамного сложнее:
private Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open();
} catch (Exception e) {
Toast.makeText(this, “Увы, камеры нет!”, Toast.LENGTH_LONG);
Log.e(TAG, “Нет камеры: исключение “ + e.getMessage());
e.getStackTrace();
finish();
}
return c;
}
Метод Camera.open() обращается к первой камере на задней поверхности устройства.
Камер на устройствах Android может быть несколько; для доступа к конкретной камере воспользуйтесь методом Camera.open(int cameraId). Метод Camera.getNumberOfCameras() вернет количество камер устройства, а метод Camera. getCameraInfo() – информацию о заданной камере. Для большинства задач нам подойдет первая камера на задней поверхности устройства.
В следующих разделах мы займемся просмотром изображения с камеры и созданием снимка. Но перед этим нужно сделать еще одну важную вещь: закончив работу с камерой, освободите ее.
Иначе другие процессы не смогут получить доступ к камере – это плохая практика; кроме того, это обеспокоит пользователей.
Внесем нужные изменения в метод onPause():
@Override protected void onPause() {
super.onPause();
releaseCamera();
}
private void releaseCamera() {
if (camera != null) {
camera.release();
camera = null;
}
}
@Override protected void onResume() {
if (camera == null) {
camera.getCameraInstance();
}
super.onResume();
}
Когда мы создадим код просмотра изображения с камеры, данный метод придется несколько усложнить, но мы займемся этим потом. А пока вам важнее всего помнить, что следует обязательно освобождать камеру, когда наше приложение приостанавливается.
Создаем быстрый снимок
Если нужно сделать быстрый снимок, пригодится Намерение:
{
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
i.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(i, CAPTURE_IMAGE_ACTIVITY_REQ);
}
protected Uri getOutputMediaFileUri(int type) {
// См. код в основном тексте для getOutputMediaFile()
return Uri.fromFile(getOutputMediaFile(type));
}
Поместите первый фрагмент кода в подходящее место; можно связать его с кнопкой, поместив в метод onClick() (как в коде на нашем DVD) или сделав пунктом меню. В этом фрагменте кода создается новое Намерение – намерение по умолчанию для создания снимка – и оно связывается с URI, указывающим, где сохранить результирующее изображение. Затем мы запускаем Намерение для запуска Занятия. Нам также нужно обработать результат:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQ) {
if (resultCode == RESULT_OK) {
if (data == null) {
// Здесь замечена ошибка! Снимок должен сохраниться в fileUri
Toast.makeText(this, “Снимок успешно сохранен”,
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, “Снимок успешно сохранен: “ + data.getData(),
Toast.LENGTH_LONG).show();
}
// Здесь вы можете сделать что-нибудь еще...
} else if (resultCode == RESULT_CANCELED){
// Пользователь отменил операцию; ничего не делаем
} else {
Toast.makeText(this, “Отказ вызова захвата изображения!”,
Toast.LENGTH_LONG).show();
}
}
}
На практике вы, наверное, захотите как-то обработать изображение. В некоторых устройствах замечена ошибка, связанная с сохранением изображения посредством ПО камеры. URI изображения должен быть возвращен вместе с Намерением. Однако иногда возвращается пустое Намерение, хотя файл был сохранен правильно. Чтобы обойти эту проблему, сохраните URi изображения, который вы отправляли с Намерением, и снова воспользуйтесь им при получении успешного результата.
Просмотр изображения с камеры
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
С помощью Camera API можно сделать снимок так, что пользователь ни о чем не узнает, но в подавляющем большинстве случаев сначала нужно показать изображение с камеры, а затем пользователь нажмет на кнопку. Для этого создадим собственный класс CameraPreview, унаследованный от SurfaceView:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = “CameraPreview”;
private SurfaceHolder sh;
private Camera camera;
public CameraPreview(Context context, Camera cm) {
super(context);
camera = cm;
sh = getHolder();
sh.addCallback(this);
// устаревший, но требуемый pre-3.0
sh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
Log.e(TAG, “Ошибка установки предпросмотра: “ + e.getMessage());
e.getStackTrace();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (sh.getSurface() == null) {
// Нет поверхности предпросмотра!
return;
}
// Остановка предпросмотра до внесения изменений.
try {
camera.stopPreview();
} catch (Exception e) {
// Попытка пресечь несуществующий предпросмотр
}
try {
camera.setPreviewDisplay(sh);
camera.startPreview();
} catch (Exception e) {
Log.e(TAG, “Ошибка при рестарте предпросмотра: ” + e.getMessage());
e.getStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Занятие следит за освобождением предпросмотра камеры
}
}
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Этот класс унаследован от SurfaceView, осуществляющего рендеринг гораздо быстрее, чем стандартный View (и отрисовка может выполняться фоновыми нитями, чего не допускает View), что идеально для быстрой отрисовки, которая необходима нам для просмотра изображения с камеры.
Для доступа к поверхности изображения нам понадобится также реализовать интерфейс SurfaceHolder.CallBack. Здесь необходимы методы surfaceCreated(), SurfaceChanged() и surfaceDestroyed().
Мы получаем SurfaceHolder (с помощью метода родительского класса), затем добавим к ней Callback, который проинформирует клиента о любых изменениях с поверхностью.
Наша версия surfaceChanged() ничего не меняет. Желая ввести изменения, делайте это между остановкой и запуском предпросмотра. Одна из ситуаций, когда это может пригодиться – обработка изменения положения устройства (камеры и экрана) в пространстве.
В устройствах с Android версии до 2.2 предпросмотр автоматически включался как Пейзаж, и для максимальной совместимости проще всего принудительно установить режим Пейзажа, добавив атрибут к элементу activity в AndroidManifest.xml:
<activity [ ... ] android:screenOrientation=”landscape” >
Однако в версиях с 2.2 и выше это можно убрать, взамен задав положение просмотра согласно положению устройства. Добавьте такие строки в метод surfaceChanged() в CameraPreview.java между вызовами stopPreview() и startPreview():
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
camera.setDisplayOrientation(90);
}
Не забудьте удалить настройку пейзажа из AndroidManifest.xml, и вы увидите, что предпросмотр поворачивается вслед за поворотом камеры.
Создав класс CameraPreview, добавьте метод setUpLayout(), вызываемый из метода onCreate() в MyCameraActivity.
Здесь он выделен в отдельный метод, чтобы проще приостанавливать/возобновлять программу; подробности см. ниже.
private void setUpLayout() {
setContentView(R.layout.main);
preview = new CameraPreview(this, camera);
FrameLayout frame = (FrameLayout) findViewById(R.id.camera_preview);
frame.addView(preview);
}
Также нужно добавить кое-что в файл main.xml:
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”horizontal”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
>
<FrameLayout
android:id=”@+id/camera_preview”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:layout_weight=”1”
/>
<Button
android:id=”@+id/button_capture”
android:text=”Capture”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center”
/>
</LinearLayout>
Кодом кнопки мы займемся в следующем разделе. Скомпилируйте и запустите программу, и на экране должно появиться изображение с камеры с кнопкой Capture [Сделать снимок] рядом, хотя и при нажатии кнопки пока ничего не произойдет.
Наконец, ранее я говорила, что после написания кода просмотра методы onPause() и onResume() нужно немного усложнить. Если просто освободить камеру, то при возобновлении приложения старый класс предпросмотра тоже попробует обратиться к объекту камеры, который у него был (и который теперь освобожден), и вызовет исключение.
Чтобы это исправить, нужно освобождать объект при приостановке приложения, а при возобновлении – создавать новый:
protected void onPause() {
releaseCamera();
super.onPause();
}
private void releaseCamera() {
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
preview = null;
}
}
protected void onResume() {
if (camera == null) {
camera = getCameraInstance();
setUpLayout();
}
super.onResume();
}
Теперь вы сможете выйти из программы и затем перезапустить ее без ошибок.