package com.lan.nicehair.widget; import java.io.IOException; import java.lang.reflect.Method; import java.util.regex.Pattern; import com.lan.nicehair.utils.AppLog; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Point; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.PictureCallback; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.WindowManager; /** * 自定义拍照控件 * @author lanyj * */ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { /** LOG标识 */ private static final String TAG = "CameraPreview"; private final SurfaceHolder mHolder; private Camera mCamera=null; private OnCameraStatusListener listener; public int cameraPosition=1; private Context mContext; private Point screenResolution; private Point cameraResolution; private int previewFormat; private String previewFormatString; private static final int TEN_DESIRED_ZOOM = 27; private static final int DESIRED_SHARPNESS = 30; private static final Pattern COMMA_PATTERN = Pattern.compile(","); private static final int SDK_INT; private boolean initialized; static { int sdkInt; try { sdkInt = Integer.parseInt(Build.VERSION.SDK); } catch (NumberFormatException nfe) { // 为安全起见 sdkInt = 10000; } SDK_INT = sdkInt; } public CameraPreview(Context context,AttributeSet attrs) { super(context,attrs); this.mContext=context; mHolder = getHolder(); mHolder.addCallback(this); // 推荐的设置,但需要在Android 3以前的版本 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } @Override public void surfaceCreated(SurfaceHolder holder) { // surface已被创建,现在告诉相机在哪里画预览 try { cameraPosition=1; if(mCamera!=null) { mCamera.setPreviewCallback(null); mCamera.stopPreview();//停掉原来摄像头的预览 mCamera.release();//释放资源 } mCamera=Camera.open(); mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG,"open()方法有问题"); Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 空,记得在activity中释放摄像头 mCamera.setPreviewCallback(null); mCamera.stopPreview(); mCamera.release();//加上这句,就OK! mCamera=null; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // 如果预览可以改变或旋转,注意这里的那些事件。 // 确保在调整大小或格式时停止预览。 if (mHolder.getSurface() == null) { // 预览不存在 return; } Log.d(TAG, "camera size: " +w+" : "+h); // 在更改前停止预览 try { mCamera.stopPreview(); } catch (Exception e) { // 一个不存在的预览 } try { mCamera.setPreviewDisplay(mHolder); initParameters(); mCamera.startPreview(); } catch (Exception e) { Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } private void initParameters() { if (!initialized) { initialized = true; initFromCameraParameters(mCamera); } setDesiredCameraParameters(mCamera); } public void startCameraPreview() { mCamera.startPreview(); } public void releaseCamera() { if(mCamera!=null) { mCamera.setPreviewCallback(null); mCamera.release(); } } @SuppressLint("NewApi") public void changeCameraFacing() { //切换前后摄像头 int cameraCount = 0; CameraInfo cameraInfo = new CameraInfo(); cameraCount = Camera.getNumberOfCameras();//得到摄像头的个数 for(int i = 0; i < cameraCount; i++ ) { Camera.getCameraInfo(i, cameraInfo);//得到每一个摄像头的信息 if(cameraPosition == 1) { //现在是后置,变更为前置 if(cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置 mCamera.setPreviewCallback(null); mCamera.stopPreview();//停掉原来摄像头的预览 mCamera.release();//释放资源 mCamera = null;//取消原来摄像头 mCamera = Camera.open(i);//打开当前选中的摄像头 try { mCamera.setPreviewDisplay(mHolder);//通过surfaceview显示取景画面 initParameters(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } mCamera.startPreview();//开始预览 cameraPosition = 0; break; } } else { //现在是前置, 变更为后置 if(cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置 mCamera.setPreviewCallback(null); mCamera.stopPreview();//停掉原来摄像头的预览 mCamera.release();//释放资源 mCamera = null;//取消原来摄像头 mCamera = Camera.open(i);//打开当前选中的摄像头 try { mCamera.setPreviewDisplay(mHolder);//通过surfaceview显示取景画面 initParameters(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } mCamera.startPreview();//开始预览 cameraPosition = 1; break; } } } } // 停止拍照,并将拍摄的照片传入PictureCallback接口的onPictureTaken方法 public void takePicture() { // Log.e(TAG, "==takePicture=="); if (mCamera != null) { // 自动对焦 mCamera.autoFocus(new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { if (null != listener) { try { success=true;//我这里把他设为true了,其实 listener.onAutoFocus(success); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); success=true; } } // 自动对焦成功后才拍摄 if (success||cameraPosition==0) { camera.takePicture(null, null, pictureCallback); }else { //AppToast.showShortText(mContext, "焦距不准,请重拍!"); } } }); } } // 创建一个PictureCallback对象,并实现其中的onPictureTaken方法 private PictureCallback pictureCallback = new PictureCallback() { // 该方法用于处理拍摄后的照片数据 @Override public void onPictureTaken(byte[] data, Camera camera) { // 停止照片拍摄 AppLog.e("callback", listener+""); camera.stopPreview(); camera = null; // 调用结束事件 if (null != listener) { listener.onCameraStopped(data); } } }; // 设置监听事件 public void setOnCameraStatusListener(OnCameraStatusListener listener) { if(listener!=null) this.listener = listener; } /** * 应用程序所需要的相机值 */ void initFromCameraParameters(Camera camera) { Camera.Parameters parameters = camera.getParameters(); previewFormat = parameters.getPreviewFormat(); previewFormatString = parameters.get("preview-format"); Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString); WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); screenResolution = new Point(display.getWidth(), display.getHeight()); Log.d(TAG, "Screen resolution: " + screenResolution); cameraResolution = getCameraResolution(parameters, screenResolution); Log.d(TAG, "Camera resolution: " + screenResolution); } /** * 这部分是用的zxing解析二维码中的代码 */ void setDesiredCameraParameters(Camera camera) { Camera.Parameters parameters = camera.getParameters(); Log.d(TAG, "Setting preview size: " + cameraResolution); parameters.setPreviewSize(cameraResolution.x, cameraResolution.y); setFlash(parameters); setZoom(parameters); //setSharpness(parameters); //modify here // camera.setDisplayOrientation(90); //2.1 setDisplayOrientation(camera, 90); camera.setParameters(parameters); } private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) { String previewSizeValueString = parameters.get("preview-size-values"); // saw this on Xperia if (previewSizeValueString == null) { previewSizeValueString = parameters.get("preview-size-value"); } Point cameraResolution = null; if (previewSizeValueString != null) { Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString); cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution); } if (cameraResolution == null) { // Ensure that the camera resolution is a multiple of 8, as the screen may not be. cameraResolution = new Point( (screenResolution.x >> 3) << 3, (screenResolution.y >> 3) << 3); } return cameraResolution; } private static Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) { int bestX = 0; int bestY = 0; int diff = Integer.MAX_VALUE; for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) { previewSize = previewSize.trim(); int dimPosition = previewSize.indexOf('x'); if (dimPosition < 0) { Log.w(TAG, "Bad preview-size: " + previewSize); continue; } int newX; int newY; try { newX = Integer.parseInt(previewSize.substring(0, dimPosition)); newY = Integer.parseInt(previewSize.substring(dimPosition + 1)); } catch (NumberFormatException nfe) { Log.w(TAG, "Bad preview-size: " + previewSize); continue; } int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y); if (newDiff == 0) { bestX = newX; bestY = newY; break; } else if (newDiff < diff) { bestX = newX; bestY = newY; diff = newDiff; } } if (bestX > 0 && bestY > 0) { return new Point(bestX, bestY); } return null; } private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) { int tenBestValue = 0; for (String stringValue : COMMA_PATTERN.split(stringValues)) { stringValue = stringValue.trim(); double value; try { value = Double.parseDouble(stringValue); } catch (NumberFormatException nfe) { return tenDesiredZoom; } int tenValue = (int) (10.0 * value); if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) { tenBestValue = tenValue; } } return tenBestValue; } private void setFlash(Camera.Parameters parameters) { // FIXME: This is a hack to turn the flash off on the Samsung Galaxy. // And this is a hack-hack to work around a different value on the Behold II // Restrict Behold II check to Cupcake, per Samsung's advice //if (Build.MODEL.contains("Behold II") && // CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) { if (Build.MODEL.contains("Behold II") && SDK_INT == 3) { // 3 = Cupcake parameters.set("flash-value", 1); } else { parameters.set("flash-value", 2); } // This is the standard setting to turn the flash off that all devices should honor. parameters.set("flash-mode", "off"); } private void setZoom(Camera.Parameters parameters) { String zoomSupportedString = parameters.get("zoom-supported"); if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) { return; } int tenDesiredZoom = TEN_DESIRED_ZOOM; String maxZoomString = parameters.get("max-zoom"); if (maxZoomString != null) { try { int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString)); if (tenDesiredZoom > tenMaxZoom) { tenDesiredZoom = tenMaxZoom; } } catch (NumberFormatException nfe) { Log.w(TAG, "Bad max-zoom: " + maxZoomString); } } String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max"); if (takingPictureZoomMaxString != null) { try { int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString); if (tenDesiredZoom > tenMaxZoom) { tenDesiredZoom = tenMaxZoom; } } catch (NumberFormatException nfe) { Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString); } } String motZoomValuesString = parameters.get("mot-zoom-values"); if (motZoomValuesString != null) { tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom); } String motZoomStepString = parameters.get("mot-zoom-step"); if (motZoomStepString != null) { try { double motZoomStep = Double.parseDouble(motZoomStepString.trim()); int tenZoomStep = (int) (10.0 * motZoomStep); if (tenZoomStep > 1) { tenDesiredZoom -= tenDesiredZoom % tenZoomStep; } } catch (NumberFormatException nfe) { // continue } } // Set zoom. This helps encourage the user to pull back. // Some devices like the Behold have a zoom parameter if (maxZoomString != null || motZoomValuesString != null) { parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0)); } // Most devices, like the Hero, appear to expose this zoom parameter. // It takes on values like "27" which appears to mean 2.7x zoom if (takingPictureZoomMaxString != null) { parameters.set("taking-picture-zoom", tenDesiredZoom); } } public static int getDesiredSharpness() { return DESIRED_SHARPNESS; } /** * compatible 1.6 * @param camera * @param angle */ protected void setDisplayOrientation(Camera camera, int angle){ Method downPolymorphic; try { downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class }); if (downPolymorphic != null) downPolymorphic.invoke(camera, new Object[] { angle }); } catch (Exception e1) { } } /** * 相机拍照监听接口 */ public interface OnCameraStatusListener { // 相机拍照结束事件 void onCameraStopped(byte[] data); // 拍摄时自动对焦事件 void onAutoFocus(boolean success); } }