Работа с камерой на телефоне всегда представляла для меня интерес. Как же это все устроено… И вот мне в руки попал телефон с Android'ом. Я не преминул возможностью попробовать разобраться в этом. Вот что получилось в итоге.
Рассмотрим небольшую программу, которая позволяет делать снимки.
Все операции проводятся с помощью класса Camera.
Необходимо завести переменную
и инициализировать ее
После завершения работы с камерой необходимо сделать
в противном случае камера останется заблокированной и недоступной для других приложений.
Для обычных приложений типа фотокамеры инициализацию лучше всего производить в onResume, а освобождение в onPause.
Обязательным условием при работе с камерой является создание окна предпросмотра (preview). Это окно должно являться объектом класса Surfaceи для отображения на экране подходит SurfaceView.
Объявим
Чтобы задать preview, необходимо вызвать метод setPreviewDisplay, параметром которого является объект класса SurfaceHolder.
Чтобы включить отображение preview, вызываем
Если этого не сделать, то камера не сможет делать снимки.
Собственно для того, чтобы сделать снимок, необходимо вызвать метод
С помощью параметров (кстати, любой из них может быть null) задаются обработчики разных событий:
Вызов takePicture можно поместить непосредственно в обработчик onClick кнопки — в этом случае фотографирование произойдет сразу после нажатия на нее, но можно и воспользоваться предварительной автофокусировкой.
В этом случае задается обработчик Camera.AutoFocusCallback, в котором необходимо реализовать метод
Тогда после вызова в обработчике нажатия на кнопку camera.autoFocus(), однократно будет вызван обработчик, в котором мы уже и примем решение об удачной фокусировке и необходимости сделать снимок.
Для работы с SurfaceHolder можно задать SurfaceHolder.Callback
В этом случае необходимо реализовать методы
C помощью них приложению будет сообщаться о том, что Surface успешно создано, если оно изменено или то, что оно удалено.
Размер нашего preview можно менять в процессе выполнения программы:
Для приложения камеры удобнее всего сразу задать расположение экрана как
В противном случае нам придется, например, в surfaceCreated проверять расположение экрана и поворачивать preview с помощью, например,
Это не очень удобно, потому что поворот экрана занимает какое-то время. В этот момент происходит вызов onPause и onResume, пересоздается Surface.
Также имеется возможность объявить обработчик Camera.PreviewCallback, с помощью которого путем реализации метода
можно получать и обрабатывать каждый кадр, отображаемый в preview.
И последний важный момент. Чаще всего получается так, что отношение сторон SurfaceView отличается от отношения сторон в preview камеры. Поэтому для того, чтобы избежать искажений изображения на экране, необходимо подкорректировать размер отображаемого окна предпросмотра.
Чуть не забыл. В манифест необходимо добавить permission
MainScreen.java
main.xml
AndroidManifest.xml
Программа отлаживалась и тестировалась на телефоне LG Optimus One P500.
При написании использовались следующие источники информации:
Скачать проект можно по ссылке: перезалил вот сюда zalil.ru/30377379
Рассмотрим небольшую программу, которая позволяет делать снимки.
Все операции проводятся с помощью класса Camera.
Необходимо завести переменную
Camera camera;
и инициализировать ее
camera = Camera.open();
После завершения работы с камерой необходимо сделать
camera.release();
в противном случае камера останется заблокированной и недоступной для других приложений.
Для обычных приложений типа фотокамеры инициализацию лучше всего производить в onResume, а освобождение в onPause.
Обязательным условием при работе с камерой является создание окна предпросмотра (preview). Это окно должно являться объектом класса Surfaceи для отображения на экране подходит SurfaceView.
Объявим
SurfaceView preview;
Чтобы задать preview, необходимо вызвать метод setPreviewDisplay, параметром которого является объект класса SurfaceHolder.
SurfaceHolder surfaceHolder;
surfaceHolder = preview.getHolder();
camera.setPreviewDisplay(surfaceHolder);
Чтобы включить отображение preview, вызываем
camera.startPreview();
Если этого не сделать, то камера не сможет делать снимки.
Собственно для того, чтобы сделать снимок, необходимо вызвать метод
void takePicture(Camera.ShutterCalback shutter, Camera.PictureCallback raw, Camera.PictureCallback postview, Camera.PictureCallback jpg);
С помощью параметров (кстати, любой из них может быть null) задаются обработчики разных событий:
- shutter — вызывается в момент получения изображения с матрицы
- raw — программе передаются для обработки raw данные (если поддерживается аппаратно)
- postview — программе передаются полностью обработанные данные (если поддерживается аппаратно)
- jpg — программе передается изображение в виде jpg. Здесь можно организовать запись изображения на карту памяти.
Вызов takePicture можно поместить непосредственно в обработчик onClick кнопки — в этом случае фотографирование произойдет сразу после нажатия на нее, но можно и воспользоваться предварительной автофокусировкой.
В этом случае задается обработчик Camera.AutoFocusCallback, в котором необходимо реализовать метод
public void onAutoFocus(boolean paramBoolean, Camera paramCamera);
Тогда после вызова в обработчике нажатия на кнопку camera.autoFocus(), однократно будет вызван обработчик, в котором мы уже и примем решение об удачной фокусировке и необходимости сделать снимок.
Для работы с SurfaceHolder можно задать SurfaceHolder.Callback
surfaceHolder.addCallback();
В этом случае необходимо реализовать методы
public void surfaceCreated(SurfaceHolder holder);
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height);
public void surfaceDestroyed(SurfaceHolder holder);
C помощью них приложению будет сообщаться о том, что Surface успешно создано, если оно изменено или то, что оно удалено.
Размер нашего preview можно менять в процессе выполнения программы:
LayoutParams lp = preview.getLayoutParams();
lp.width = задаваемая ширина;
lp.height = задаваемая высота;
preview.setLayoutParams(lp);
Для приложения камеры удобнее всего сразу задать расположение экрана как
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
В противном случае нам придется, например, в surfaceCreated проверять расположение экрана и поворачивать preview с помощью, например,
camera.setDisplayOrientation(0)
.Это не очень удобно, потому что поворот экрана занимает какое-то время. В этот момент происходит вызов onPause и onResume, пересоздается Surface.
Также имеется возможность объявить обработчик Camera.PreviewCallback, с помощью которого путем реализации метода
void onPreviewFrame(byte[] paramArrayOfByte, Camera paramCamera);
можно получать и обрабатывать каждый кадр, отображаемый в preview.
И последний важный момент. Чаще всего получается так, что отношение сторон SurfaceView отличается от отношения сторон в preview камеры. Поэтому для того, чтобы избежать искажений изображения на экране, необходимо подкорректировать размер отображаемого окна предпросмотра.
Чуть не забыл. В манифест необходимо добавить permission
<uses-permission android:name="android.permission.CAMERA" />
MainScreen.java
package test.camera;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.view.View;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainScreen extends Activity implements SurfaceHolder.Callback, View.OnClickListener, Camera.PictureCallback, Camera.PreviewCallback, Camera.AutoFocusCallback
{
private Camera camera;
private SurfaceHolder surfaceHolder;
private SurfaceView preview;
private Button shotBtn;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// если хотим, чтобы приложение постоянно имело портретную ориентацию
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// если хотим, чтобы приложение было полноэкранным
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// и без заголовка
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
// наше SurfaceView имеет имя SurfaceView01
preview = (SurfaceView) findViewById(R.id.SurfaceView01);
surfaceHolder = preview.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// кнопка имеет имя Button01
shotBtn = (Button) findViewById(R.id.Button01);
shotBtn.setText("Shot");
shotBtn.setOnClickListener(this);
}
@Override
protected void onResume()
{
super.onResume();
camera = Camera.open();
}
@Override
protected void onPause()
{
super.onPause();
if (camera != null)
{
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
try
{
camera.setPreviewDisplay(holder);
camera.setPreviewCallback(this);
}
catch (IOException e)
{
e.printStackTrace();
}
Size previewSize = camera.getParameters().getPreviewSize();
float aspect = (float) previewSize.width / previewSize.height;
int previewSurfaceWidth = preview.getWidth();
int previewSurfaceHeight = preview.getHeight();
LayoutParams lp = preview.getLayoutParams();
// здесь корректируем размер отображаемого preview, чтобы не было искажений
if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE)
{
// портретный вид
camera.setDisplayOrientation(90);
lp.height = previewSurfaceHeight;
lp.width = (int) (previewSurfaceHeight / aspect);
;
}
else
{
// ландшафтный
camera.setDisplayOrientation(0);
lp.width = previewSurfaceWidth;
lp.height = (int) (previewSurfaceWidth / aspect);
}
preview.setLayoutParams(lp);
camera.startPreview();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
}
@Override
public void onClick(View v)
{
if (v == shotBtn)
{
// либо делаем снимок непосредственно здесь
// либо включаем обработчик автофокуса
//camera.takePicture(null, null, null, this);
camera.autoFocus(this);
}
}
@Override
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera)
{
// сохраняем полученные jpg в папке /sdcard/CameraExample/
// имя файла - System.currentTimeMillis()
try
{
File saveDir = new File("/sdcard/CameraExample/");
if (!saveDir.exists())
{
saveDir.mkdirs();
}
FileOutputStream os = new FileOutputStream(String.format("/sdcard/CameraExample/%d.jpg", System.currentTimeMillis()));
os.write(paramArrayOfByte);
os.close();
}
catch (Exception e)
{
}
// после того, как снимок сделан, показ превью отключается. необходимо включить его
paramCamera.startPreview();
}
@Override
public void onAutoFocus(boolean paramBoolean, Camera paramCamera)
{
if (paramBoolean)
{
// если удалось сфокусироваться, делаем снимок
paramCamera.takePicture(null, null, null, this);
}
}
@Override
public void onPreviewFrame(byte[] paramArrayOfByte, Camera paramCamera)
{
// здесь можно обрабатывать изображение, показываемое в preview
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:id="@+id/FrameLayout01"
android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<SurfaceView android:id="@+id/SurfaceView01"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</SurfaceView>
<Button android:text="@+id/Button01" android:id="@+id/Button01"
android:layout_width="wrap_content" android:layout_height="wrap_content">
</Button>
</FrameLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.camera"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true">
<activity android:name=".MainScreen" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
Программа отлаживалась и тестировалась на телефоне LG Optimus One P500.
При написании использовались следующие источники информации:
- developer.android.com/reference/android/hardware/Camera.html
- Shawn Van Every. Pro Android Media: Developing Graphics, Music, Video and Rich Media Apps for Smartfones and Tablets. Apress 2009.
- developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/graphics/CameraPreview.html
Скачать проект можно по ссылке: перезалил вот сюда zalil.ru/30377379