Работа с камерой на телефоне всегда представляла для меня интерес. Как же это все устроено… И вот мне в руки попал телефон с Android'ом. Я не преминул возможностью попробовать разобраться в этом. Вот что получилось в итоге.
Рассмотрим небольшую программу, которая позволяет делать снимки.
Все операции проводятся с помощью класса 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
<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
<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