实现自定义相机(camera的使用)
实现步骤
1.获取一个相机对象(Camera)
2.设置相机相关的回调和参数
3.创建一个预览界面(SurfaceView)、
4.捕捉一张图片(mCamera.takePicture(null, null, mPicture);)
5.保存图片
6.释放相机对象
虽然步骤很简单,但是设计到和硬件打交道就会设计到兼容性的问题,同事Android平台下碎片化严重,实现过程中还是会遇到很多坑。为了大家避免一些不必要的坑,我简单是现实了一个简单的demo。
具体代码如下
public class CameraActivity extends Activity {
private static final int BUFFER_SIZE = 8 * 1024;
private CameraPreview mPreview;
private Button btn_take_picture;
private Camera mCamera;
private FrameLayout fl_camera_preview;
private String imgOutputPath;
private SurfaceHolder mHolder = null;
private ImageView iv_flashLight;
private View.OnClickListener mOnclickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_take_picture:
takePicture();
break;
case R.id.iv_flashLight:
CameraUtils.toggleFlashLight(mCamera);
break;
}
}
};
private void takePicture() {
if (null != mCamera) {
mCamera.takePicture(null, null, mPicture);
}
}
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
savePicture(data);
setResult(RESULT_OK);
CameraActivity.this.finish();
}
};
private Camera.AutoFocusCallback mAutoFocusCallback = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
KSLog2.KSLog().d("auto focus success!");
} else {
KSLog2.KSLog().d("auto focus failed!");
}
}
};
private CameraPreview.OnZoomListener zoomListener = new CameraPreview.OnZoomListener() {
@Override
public void onZoom(final float scaleFactor) {
if (null == mCamera) return;
mCamera.setParameters(CameraUtils.setZoom(mCamera.getParameters(), scaleFactor));
}
};
private SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
if (null == mCamera) return;
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
// mCamera.autoFocus(mAutoFocusCallback);
} catch (IOException e) {
e.printStackTrace();
KSLog2.KSLog().d("Error setting camera preview: " + e.getMessage());
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
KSLog2.KSLog().d("surfaceDestroyed");
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
if (null == mCamera) return;
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
e.printStackTrace();
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
KSLog2.KSLog().d("Error starting camera preview: " + e.getMessage());
}
}
};
private void savePicture(byte[] data) {
if (TextUtils.isEmpty(imgOutputPath)) {
KSLog2.KSLog().w("imgOutputPath is empty");
return;
}
File pictureFile = new File(imgOutputPath);
try {
OutputStream os = new BufferedOutputStream(new FileOutputStream(pictureFile), BUFFER_SIZE);
os.write(data);
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
parseIntentParams();
PrepareView();
KSLog2.KSLog().d("CameraActivity onCreate");
}
private void PrepareView() {
btn_take_picture = (Button) findViewById(R.id.btn_take_picture);
fl_camera_preview = (FrameLayout) findViewById(R.id.fl_camera_preview);
iv_flashLight = (ImageView) findViewById(R.id.iv_flashLight);
mPreview = new CameraPreview(this);
mHolder = mPreview.getHolder();
mHolder.addCallback(callback);
btn_take_picture.setOnClickListener(mOnclickListener);
iv_flashLight.setOnClickListener(mOnclickListener);
mPreview.setOnZoomListener(zoomListener);
}
private void parseIntentParams() {
Intent intent = getIntent();
Uri uri = intent.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
if (uri == null) {
throw new RuntimeException("EXTRA_OUTPUT is null");
}
KSLog2.KSLog().d("uri path = " + uri.getPath());
imgOutputPath = uri.getPath();
}
@Override
protected void onResume() {
super.onResume();
// Create our Preview view and set it as the content of our activity.
mCamera = CameraUtils.getCameraInstance(this);
fl_camera_preview.addView(mPreview);
}
@Override
protected void onPause() {
super.onPause();
releaseCamera(); // release the camera immediately on pause event
fl_camera_preview.removeView(mPreview);
}
public void releaseCamera() {
if (mCamera != null) {
mCamera.release(); // release the camera for other applications
mCamera = null;
}
}
}
CameraUtils.java(相机工具类)
public class CameraUtils {
public static final int PREVIEW_WIDTH = 720;
public static final int PREVIEW_HEIGHT = 1280;
public static final int PICTURE_WIDTH = 720;
public static final int PICTURE_HEIGHT = 1280;
private static void configCamera(Activity activity, Camera camera) {
if (camera == null) return;
// get Camera parameters
Camera.Parameters params = camera.getParameters();
//set preview size
setPreviewWH(params);
// set the focus mode
setFocusMode(params);
// set picture size
setPictureSize(params);
// set picture format
params.setPictureFormat(ImageFormat.JPEG);
// set Camera parameters
camera.setParameters(params);
camera.setDisplayOrientation(getCorrectOrientation(activity));
}
/**
* A safe way to get an instance of the Camera object.
*/
public static Camera getCameraInstance(Activity activity) {
Camera camera = null;
try {
camera = Camera.open(); // attempt to get a Camera instance
configCamera(activity, camera);
} catch (Exception e) {
e.printStackTrace();
// Camera is not available (in use or does not exist)
}
return camera; // returns null if camera is unavailable
}
/**
* Check if this device has a camera
*/
public static boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
private static void setPreviewWH(Camera.Parameters params) {
List<Camera.Size> previewsSizes = params.getSupportedPreviewSizes();
Collections.sort(previewsSizes, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size lhs, Camera.Size rhs) {
if ((lhs.width * lhs.height) > (rhs.width * rhs.height)) {
return 1;
} else {
return -1;
}
}
});
for (Camera.Size size : previewsSizes) {
if (size.width >= PREVIEW_WIDTH && size.height >= PREVIEW_HEIGHT) {
params.setPreviewSize(size.width, size.height);
KSLog2.KSLog().d("preview width = " + size.width + " preview height = " + size.height);
return;
}
}
}
private static void setFocusMode(Camera.Parameters params) {
List<String> focusModes = params.getSupportedFocusModes();
if (focusModes != null) {
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
} else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) {
params.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
}
}
}
private static void setPictureSize(Camera.Parameters params) {
List<Camera.Size> PictureSize = params.getSupportedPictureSizes();
Collections.sort(PictureSize, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size lhs, Camera.Size rhs) {
if ((lhs.width * lhs.height) > (rhs.width * rhs.height)) {
return -1;
} else {
return 1;
}
}
});
for (Camera.Size size : PictureSize) {
if (size.width >= PICTURE_WIDTH && size.height >= PICTURE_HEIGHT) {
params.setPictureSize(size.width, size.height);
KSLog2.KSLog().d("picture width = " + size.width + " picture height = " + size.height);
return;
}
}
}
public static Camera.Parameters setZoom(Camera.Parameters params, float scaleFactor) {
if (!params.isZoomSupported()) return params;
try {
int addValue = (int) ((scaleFactor - 1.0f) * params.getMaxZoom());
int value = params.getZoom() + addValue;
if (value > params.getMaxZoom()) {
value = params.getMaxZoom();
}
if (value < 0) {
value = 0;
}
KSLog2.KSLog().d("zoom value = " + value + "scaleFactor = " + scaleFactor);
params.setZoom(value);
} catch (Exception e) {
e.printStackTrace();
}
return params;
}
public static boolean toggleFlashLight(Camera camera) {
if (null == camera) return false;
try {
Camera.Parameters parameters = camera.getParameters();
List<String> flashModes = parameters.getSupportedFlashModes();
String flashMode = parameters.getFlashMode();
if (!Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
camera.setParameters(parameters);
return true;
}
} else if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
KSLog2.KSLog().d("toggleFlashLight,failed" + e.getMessage());
return false;
}
return false;
}
private static int getCorrectOrientation(Activity activity) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(findFacingBackCameraID(), info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
// 记录本机子相机的旋转角度
return result;
}
private static int findFacingBackCameraID() {
int cameraId = -1;
// Search for the back facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
KSLog2.KSLog().d("Camera found");
cameraId = i;
break;
}
}
return cameraId;
}
}
CameraPreview.java(负责显示相机预览图片)
public class CameraPreview extends SurfaceView {
private final ScaleGestureDetector scaleGestureDetector;
private CameraPreview.OnZoomListener mListener;
public interface OnZoomListener {
public void onZoom(float scaleFactor);
}
private ScaleGestureDetector.OnScaleGestureListener onScaleGestureListener = new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactor = detector.getScaleFactor();
if(null != mListener) {
mListener.onZoom(scaleFactor);
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
};
@Override
public boolean onTouchEvent(MotionEvent event) {
KSLog2.KSLog().d(" onTouchEvent");
if (scaleGestureDetector.onTouchEvent(event)) {
return true;
}
return super.onTouchEvent(event);
}
public CameraPreview(Context context) {
super(context);
scaleGestureDetector = new ScaleGestureDetector(context, onScaleGestureListener);
}
public void setOnZoomListener(OnZoomListener mListener) {
this.mListener = mListener;
}
}
布局代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_take_picture"
android:text="Capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_gravity="center"/>
<FrameLayout
android:layout_above="@+id/btn_take_picture"
android:id="@+id/fl_camera_preview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<ImageView
android:id="@+id/iv_flashLight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="@drawable/public_icon"/>
</RelativeLayout>