В некоторых приложениях нужно получить снимок с камеры, и при этом, хотелось бы, чтобы камера была нестандартная,
со своими кнопками и другими элементами управления. Читаем дальше и узнаем, как это сделать и какие проблемы могут возникнуть.

Создание класса области просмотра камеры

Для создания области просмотра, нам нужно создать класс, который будет наследовать от SurfaceView.
SurfaceView используется для рисования чего-либо на экране, в данном случае на нем будет рисовать свое изображение камера.
Нам важно подписаться на уведомления создания и удаления SurfaceView, чтобы запускать и останавливать камеру соответственно.
import java.io.IOException;
import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

private SurfaceHolder mSurfaceHolder;
private Camera mCamera;

public CameraPreview(Context context, Camera camera) {
super(context);
this.mCamera = camera;
this.mSurfaceHolder = this.getHolder();
this.mSurfaceHolder.addCallback(this); // вот здесь мы подписались на уведомления о создании и удалении
this.mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (IOException e) { }
}

@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mCamera.stopPreview();
mCamera.release();
}

@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
try {
Camera.Parameters parameters = mCamera.getParameters();
//для тестов выводим поддерживаемые размеры
for(Camera.Size cz : parameters.getSupportedPreviewSizes())
Log.d("camera", String.valueOf(cz.width) + "/" + String.valueOf(cz.height));
int pictureWidth = 720; // можно поставить любые поддерживаемые размеры камеры
int pictureHeight = 480;
parameters.setPreviewSize(pictureWidth, pictureHeight);
parameters.setPictureSize(pictureWidth, pictureHeight);
mCamera.setParameters(parameters);
mCamera.setDisplayOrientation(0); //таким образом мы можем вращать область просмотра, в данном случае landscape
mCamera.setPreviewDisplay(surfaceHolder);
mCamera.startPreview();
} catch (Exception e) { }
}
}

Создание шаблона Activity

Шаблон можно сделать по своему желанию, главное предусмотреть пустой Layout,
к которому мы прикрепим наш CameraPreview.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true" >
</FrameLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="66dp"
android:layout_gravity="center_horizontal|bottom" >
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="15dp"
android:background="#e6f2ff">
<Button
android:id="@+id/make_photo_btn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/make_photo_btn" />
</FrameLayout>
</RelativeLayout>
</FrameLayout>

В этом шаблоне стоит обратить внимание на использование внешнего FrameLayout.
Это позволит сделать нам просмотр на весь экран и при этом кнопка
фотографирования будет над областью просмотра.
Также у элемента camera_preview установлен атрибут android:keepScreenOn в true для того, чтобы экран не потухал во время фотографирования.

Создание Activity

Мы получаем объект камеры, создаем CameraPreview и добавляем этот объект в layout.
import java.io.FileOutputStream;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import android.widget.TextView;

public class PhotoActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.photo_activity);
final Camera mCamera = getCameraInstance();
final CameraPreview mCameraPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mCameraPreview);
final Camera.PictureCallback mPicture = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outputStream;
try {
outputStream = openFileOutput("our_file.jpg", Context.MODE_PRIVATE);
outputStream.write(data);
outputStream.close();
finish();
} catch (Exception e) {
e.printStackTrace();
}
}
};
final Camera.AutoFocusCallback mAutoFocusCallback = new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
camera.takePicture(null, null, mPicture);
}
};
Button make_photo_btn = (Button)findViewById(R.id.make_photo_btn);
make_photo_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCamera.autoFocus(mAutoFocusCallback);
}
});
}

private Camera getCameraInstance() {
Camera camera = null;
try {
camera = Camera.open();
} catch (Exception e) { }
return camera;
}
}

Тут есть одна хитрость: при нажатии на кнопку, мы не фотографируем сразу, а запускаем автофокус, и только когда он сработал, мы берем фото с камеры.
Спрашивается зачем такие манипуляции? Если Вы попробуете сразу сфотографировать, то скорее всего получится хорошее фото, но в области просмотра появится эффект размытия, что может сбить с толку пользователя.
При использовании автофокуса размытия нет.

Разрешения в AndroidManifest.xml

И окончательный шрих – это объявление разрешения на фотографирование.
<uses-permission android:name="android.permission.CAMERA" />