android 自定义相机

老规矩,先上一下项目地址:GitHub:https://github.com/xiangzhihong/CameraDemo

方式:

  • 调用Camera API 自定义相机
  • 调用系统相机

由于需求不同,所以选择的方案固然也不同,至于第二种调用系统相机,这里就不过多讲解了,使用Intent对象设置一个Action动作即可,跳转时使用startActivityForResult,然后在onActivityResult处理相关数据便可,关键代码:

 intent.setAction("android.media.action.STILL_IMAGE_CAMERA");  

至于使用,较常见的一般是应用中用户上传头像的时候调用,然后返回处理图像数据。

而第一种自定义相机的方式使用也十分普遍,但是要做好这个模块,相对来说还是有一定难度的,之前分享过一个Github上的开源相机的项目,项目由美国的一个团队开发,集 拍照、摄影、各种特效动画 等功能与一身,本人之前研究了下,发现功能比较全面也很强大,抠出来单独拍照那一个模块,我滴妈呀,真TM费劲!相机不管是预览还是拍摄图像都还是很清晰的,自己当时也写了一个,比较操蛋,只能怪自己对这一块的优化了解浅显吧!特别是预览的时候,聚焦完成后,焦点周边会出现很多白色的噪点,密密麻麻,特别严重,头疼的很。不过也总算解决了,灰常感谢USA的那个什么什么团队的开源相机程序。经过自己改造后的预览效果图:

下面看下这个项目的效果图,我也把地址甩底下,大伙感兴趣的自行Clone研究(或者闲的蛋疼也可以抽时间剥离开每一个模块学习,作为日后的知识储备),里面也用到了这个Android中读取图片EXIF元数据之metadata-extractor的使用

相机开发简介

下面说说在Android中调用Camera来定义相机的最基本步骤:

  1. 打开相机 —— 调用Camera的open()方法。
  2. 获取拍照参数 —— 调用Camera的getParameters()方法,返回Camera.Parameters对象。
  3. 拍照参数设置 —— 调用Camera.Parameters对象。
  4. 拍照参数控制 —— 调用Camera的setParameters(),并将Camera.Parameters对象作为参数传入。注:Android2.3.3之后不用设置。
  5. 预览取景 —— 调用Camera的startPreview()方法,在之前注意调用Camera的setPreviewDisplay(SurfaceHolder holder)设置使用哪个SurfaceView来显示取得的图片。
  6. 拍照 —— 调用Camera的takePicture()
  7. 停止预览 —— 调用Camera的stopPreview()方法
  8. 资源释放 —— Camera.release()

开启和关闭预览的联系如下:Camera ---- SurfaceHolder ------ SurfaceView

关于SurfaceHolder.Callback必须实现的3个方法:

surfaceCreated() 该方法在surfaceView被Create时调用 surfaceChanged() 该方法是当surfaceView发生改变后调用 surfaceDestroyed() 这个不用说了,销毁时调用 surfaceHolder通过addCallBack()方法将响应的接口绑定

注:必要Camera权限,例如:

 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 
  
 <uses-permission android:name="android.permission.CAMERA"/> 
  
 <uses-feature android:name="android.hardware.camera" /> 
  
 <uses-permission android:name="android.hardware.camera.autofocus" /> 
  
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

关于Camera下的Parameters类,其中封装了我们需要的大部分功能,下面做个简单介绍:

  1. setPictureFormat() 方法用于设置相机照片的格式,其参数是一个字符型参数,位于PixelFormat类中,如:PixelFormat.JPEG。
  2. setSceneMode() 方法用于设置相机场景类型,其参是是一个字符型参数,位于Parameters类中,以SCENE_MODE_开头。
  3. setZoom() 方法用于设置相机焦距,其参数是一个整型的参数,该参数的范围是0到Camera.getParameters().getMaxZoom()。
  4. setPictureSize() 方法用于设置相机照片的大小,参数为整型。
  5. setWhiteBalance() 方法用于设置相机照片白平衡,其参数是一个字符型,位于Parameters类中,以WHITE_BALANCE开头。
  6. setJpegQuality() 方法用于设置相机照片的质量,其参数是一个整型参数,取值范围为1到100。
  7. setFlashMode() 方法用于设置闪光灯的类型,其参数是一个字符型参数,位于Parameters类中,以FLASH_MODE_开头。
  8. setColorEffect() 方法用于设置照片颜色特效的类型,其参数是一个字符型参数,位于Parameters类中,以EFFECT_开头。

本程序模块效果图及示例

下面分享本篇Blog的示例相机模块,此功能模块并非上面开源项目中的剥离出来的,看下效果图咯:

效果看着还可以吧(不点赞也太不给面子了吧  - . - ),下面个出主界面的布局代码:

 <?xml version="1.0" encoding="utf-8"?> 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:id="@+id/layout" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" > 
  
  <!-- 预览画布 --> 
  <SurfaceView 
  android:id="@+id/surfaceView" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" /> 
  
  <!-- 闪光灯、前置摄像头、后置摄像头、聚焦 --> 
  
  <RelativeLayout 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" > 
  
  <org.gaochun.camera.CameraGrid 
  android:id="@+id/camera_grid" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:layout_alignParentTop="true" /> 
  
  <View 
  android:id="@+id/focus_index" 
  android:layout_width="40dp" 
  android:layout_height="40dp" 
  android:background="@drawable/camera_focus" 
  android:visibility="invisible" /> 
  
  <ImageView 
  android:id="@+id/flash_view" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentLeft="true" 
  android:onClick="onClick" 
  android:padding="15dp" 
  android:scaleType="centerCrop" 
  android:src="@drawable/camera_flash_off" /> 
  
  <ImageView 
  android:id="@+id/camera_flip_view" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentRight="true" 
  android:onClick="onClick" 
  android:padding="15dp" 
  android:scaleType="centerCrop" 
  android:src="@drawable/camera_flip" /> 
  
  <!-- 底部按钮 --> 
  
  <RelativeLayout 
  android:layout_width="fill_parent" 
  android:layout_height="70dp" 
  android:layout_alignParentBottom="true" 
  android:background="#a0000000" 
  android:padding="5dp" > 
  
  <Button 
  android:id="@+id/search" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_marginLeft="30dp" 
  android:background="@null" 
  android:drawablePadding="3dp" 
  android:drawableTop="@drawable/ic_search_selector" 
  android:onClick="onClick" 
  android:text="搜图" 
  android:textColor="@drawable/row_selector_text" /> 
  
  <ImageView 
  android:id="@+id/action_button" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_centerInParent="true" 
  android:clickable="true" 
  android:onClick="onClick" 
  android:src="@drawable/btn_shutter_photo" /> 
  
  <Button 
  android:id="@+id/takephoto" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentRight="true" 
  android:layout_marginRight="30dp" 
  android:background="@null" 
  android:drawablePadding="3dp" 
  android:drawableTop="@drawable/ic_takephoto_selector" 
  android:onClick="onClick" 
  android:text="拍照" 
  android:textColor="@drawable/row_selector_text" /> 
  </RelativeLayout> 
  </RelativeLayout> 
  
 </FrameLayout> 

下面是核心模块 CameraPreview 类:

 public class CameraPreview extends ViewGroup implements SurfaceHolder.Callback, Camera.AutoFocusCallback {  
  
  private SurfaceView mSurfaceView;  
  private SurfaceHolder mHolder;  
  private Size mPreviewSize;  
  private Size adapterSize;  
  //private List<Size> mSupportedPreviewSizes; 
  private Camera mCamera;  
  private boolean isSupportAutoFocus = false;  
  private Camera.Parameters parameters = null;  
  private Context mContext;  
  //private int mCurrentCameraId = 0; 
  private int screenWidth;  
  private int screenHeight;  
  
     CameraPreview(Context context, SurfaceView sv) {  
  super(context);  
         mContext = context;  
         mSurfaceView = sv;  
         mHolder = mSurfaceView.getHolder();  
         mHolder.addCallback(this);  
         mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
         mHolder.setKeepScreenOn(true);  
         isSupportAutoFocus = context.getPackageManager().hasSystemFeature(  
                 PackageManager.FEATURE_CAMERA_AUTOFOCUS);  
         DisplayMetrics dm = new DisplayMetrics();  
         ((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(dm);  
         screenWidth = dm.widthPixels;  
         screenHeight = dm.heightPixels;  
     }  
  
  public void setCamera(Camera camera) {  
         mCamera = camera;  
         initCamera();  
     }  
  
  public void initCamera() {  
  if (mCamera != null) {  
             Camera.Parameters params = mCamera.getParameters();  
  //mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); 
             requestLayout();  
  if (mPreviewSize == null) {  
                 mPreviewSize = findBestPreviewResolution();  
             }  
  if (adapterSize == null) {  
                 adapterSize = findBestPictureResolution();  
             }  
  if (adapterSize != null) {  
                 params.setPictureSize(adapterSize.width, adapterSize.height);  
             }  
  if (mPreviewSize != null) {  
                 params.setPreviewSize(mPreviewSize.width, mPreviewSize.height);  
             }  
             params.setPictureFormat(PixelFormat.JPEG);  
             List<String> focusModes = params.getSupportedFocusModes();  
  if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {  
  // set the focus mode 
                 params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);  
  // set Camera parameters 
                 mCamera.setParameters(params);  
             }  
             setDispaly(params, mCamera);  
  //setCameraDisplayOrientation((Activity) mContext, mCurrentCameraId, mCamera); 
             mCamera.setParameters(params);  
         }  
     }  
  
  //控制图像的正确显示方向 
  private void setDispaly(Camera.Parameters parameters, Camera camera) {  
  if (Build.VERSION.SDK_INT >= 8) {  
             setDisplayOrientation(camera, 90);  
         } else {  
             parameters.setRotation(90);  
         }  
     }  
  
  //实现的图像的正确显示 
  private void setDisplayOrientation(Camera camera, int i) {  
         Method downPolymorphic;  
  try {  
             downPolymorphic = camera.getClass().getMethod("setDisplayOrientation",  
  new Class[]{int.class});  
  if (downPolymorphic != null) {  
                 downPolymorphic.invoke(camera, new Object[]{i});  
             }  
         } catch (Exception e) {  
             e.printStackTrace();  
         }  
     }  
  
  public static void setCameraDisplayOrientation(Activity activity,  
  int cameraId, android.hardware.Camera camera) {  
         android.hardware.Camera.CameraInfo info =  
  new android.hardware.Camera.CameraInfo();  
         android.hardware.Camera.getCameraInfo(cameraId, 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;  
         }  
         camera.setDisplayOrientation(result);  
     }  
  
  @Override 
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);  
  final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);  
         setMeasuredDimension(width, height);  
  //        if (mSupportedPreviewSizes != null) { 
  //             mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height); 
  //        } 
     }  
  
  @Override 
  protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  if (changed && getChildCount() > 0) {  
  final View child = getChildAt(0);  
  
  final int width = r - l;  
  final int height = b - t;  
  
  int previewWidth = width;  
  int previewHeight = height;  
  if (mPreviewSize != null) {  
                 previewWidth = mPreviewSize.width;  
                 previewHeight = mPreviewSize.height;  
             }  
  
  // Center the child SurfaceView within the parent. 
  if (width * previewHeight > height * previewWidth) {  
  final int scaledChildWidth = previewWidth * height / previewHeight;  
                 child.layout((width - scaledChildWidth) / 2, 0,  
                         (width + scaledChildWidth) / 2, height);  
             } else {  
  final int scaledChildHeight = previewHeight * width / previewWidth;  
                 child.layout(0, (height - scaledChildHeight) / 2,  
                         width, (height + scaledChildHeight) / 2);  
             }  
         }  
     }  
  
  public void surfaceCreated(SurfaceHolder holder) {  
  // The Surface has been created, acquire the camera and tell it where 
  // to draw. 
  try {  
  if (mCamera != null) {  
                 mCamera.setPreviewDisplay(holder);  
             }  
         } catch (IOException e) {  
  if (null != mCamera) {  
                 mCamera.release();  
                 mCamera = null;  
  
             }  
             e.printStackTrace();  
         }  
     }  
  
  public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {  
  if (holder.getSurface() == null) {  
  return;  
         }  
  if (mCamera != null) {  
             Camera.Parameters parameters = mCamera.getParameters();  
             parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);  
             mCamera.setParameters(parameters);  
  try {  
                 mCamera.setPreviewDisplay(holder);  
             } catch (IOException e) {  
                 e.printStackTrace();  
             }  
             mCamera.startPreview();  
             reAutoFocus();  
         }  
     }  
  
  public void surfaceDestroyed(SurfaceHolder holder) {  
  // Surface will be destroyed when we return, so stop the preview. 
  if (mCamera != null) {  
             mCamera.stopPreview();  
         }  
     }  
  
  /** 
      * 最小预览界面的分辨率 
      */ 
  private static final int MIN_PREVIEW_PIXELS = 480 * 320;  
  /** 
      * 最大宽高比差 
      */ 
  private static final double MAX_ASPECT_DISTORTION = 0.15;  
  
  /** 
      * 找出最适合的预览界面分辨率 
      * 
      * @return 
      */ 
  private Camera.Size findBestPreviewResolution() {  
         Camera.Parameters cameraParameters = mCamera.getParameters();  
         Camera.Size defaultPreviewResolution = cameraParameters.getPreviewSize();  
  
         List<Camera.Size> rawSupportedSizes = cameraParameters.getSupportedPreviewSizes();  
  if (rawSupportedSizes == null) {  
  return defaultPreviewResolution;  
         }  
  
  // 按照分辨率从大到小排序 
         List<Camera.Size> supportedPreviewResolutions = new ArrayList<Camera.Size>(rawSupportedSizes);  
         Collections.sort(supportedPreviewResolutions, new Comparator<Size>() {  
  @Override 
  public int compare(Camera.Size a, Camera.Size b) {  
  int aPixels = a.height * a.width;  
  int bPixels = b.height * b.width;  
  if (bPixels < aPixels) {  
  return -1;  
                 }  
  if (bPixels > aPixels) {  
  return 1;  
                 }  
  return 0;  
             }  
         });  
  
         StringBuilder previewResolutionSb = new StringBuilder();  
  for (Camera.Size supportedPreviewResolution : supportedPreviewResolutions) {  
             previewResolutionSb.append(supportedPreviewResolution.width).append('x').append(supportedPreviewResolution.height)  
             .append(' ');  
         }  
  
  
  // 移除不符合条件的分辨率 
  double screenAspectRatio = (double) screenWidth  
         / screenHeight;  
         Iterator<Size> it = supportedPreviewResolutions.iterator();  
  while (it.hasNext()) {  
             Camera.Size supportedPreviewResolution = it.next();  
  int width = supportedPreviewResolution.width;  
  int height = supportedPreviewResolution.height;  
  
  // 移除低于下限的分辨率,尽可能取高分辨率 
  if (width * height < MIN_PREVIEW_PIXELS) {  
                 it.remove();  
  continue;  
             }  
  
  // 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率 
  // 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height 
  // 因此这里要先交换然preview宽高比后在比较 
  boolean isCandidatePortrait = width > height;  
  int maybeFlippedWidth = isCandidatePortrait ? height : width;  
  int maybeFlippedHeight = isCandidatePortrait ? width : height;  
  double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;  
  double distortion = Math.abs(aspectRatio - screenAspectRatio);  
  if (distortion > MAX_ASPECT_DISTORTION) {  
                 it.remove();  
  continue;  
             }  
  
  // 找到与屏幕分辨率完全匹配的预览界面分辨率直接返回 
  if (maybeFlippedWidth == screenWidth  
                     && maybeFlippedHeight == screenHeight) {  
  return supportedPreviewResolution;  
             }  
         }  
  
  
  // 如果没有找到合适的,并且还有候选的像素,则设置其中最大比例的,对于配置比较低的机器不太合适 
  if (!supportedPreviewResolutions.isEmpty()) {  
             Camera.Size largestPreview = supportedPreviewResolutions.get(0);  
  return largestPreview;  
         }  
  
  
  // 没有找到合适的,就返回默认的 
  
  return defaultPreviewResolution;  
     }  
  
  
  private Camera.Size findBestPictureResolution() {  
         Camera.Parameters cameraParameters = mCamera.getParameters();  
         List<Camera.Size> supportedPicResolutions = cameraParameters.getSupportedPictureSizes(); // 至少会返回一个值 
  
         StringBuilder picResolutionSb = new StringBuilder();  
  for (Camera.Size supportedPicResolution : supportedPicResolutions) {  
             picResolutionSb.append(supportedPicResolution.width).append('x')  
             .append(supportedPicResolution.height).append(" ");  
         }  
  
         Camera.Size defaultPictureResolution = cameraParameters.getPictureSize();  
  
  // 排序 
         List<Camera.Size> sortedSupportedPicResolutions = new ArrayList<Camera.Size>(  
                 supportedPicResolutions);  
         Collections.sort(sortedSupportedPicResolutions, new Comparator<Camera.Size>() {  
  @Override 
  public int compare(Camera.Size a, Camera.Size b) {  
  int aPixels = a.height * a.width;  
  int bPixels = b.height * b.width;  
  if (bPixels < aPixels) {  
  return -1;  
                 }  
  if (bPixels > aPixels) {  
  return 1;  
                 }  
  return 0;  
             }  
         });  
  
  
  // 移除不符合条件的分辨率 
  double screenAspectRatio = screenWidth  
         / (double) screenHeight;  
         Iterator<Camera.Size> it = sortedSupportedPicResolutions.iterator();  
  while (it.hasNext()) {  
             Camera.Size supportedPreviewResolution = it.next();  
  int width = supportedPreviewResolution.width;  
  int height = supportedPreviewResolution.height;  
  
  // 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率 
  // 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height 
  // 因此这里要先交换然后在比较宽高比 
  boolean isCandidatePortrait = width > height;  
  int maybeFlippedWidth = isCandidatePortrait ? height : width;  
  int maybeFlippedHeight = isCandidatePortrait ? width : height;  
  double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;  
  double distortion = Math.abs(aspectRatio - screenAspectRatio);  
  if (distortion > MAX_ASPECT_DISTORTION) {  
                 it.remove();  
  continue;  
             }  
         }  
  
  // 如果没有找到合适的,并且还有候选的像素,对于照片,则取其中最大比例的,而不是选择与屏幕分辨率相同的 
  if (!sortedSupportedPicResolutions.isEmpty()) {  
  return sortedSupportedPicResolutions.get(0);  
         }  
  
  // 没有找到合适的,就返回默认的 
  return defaultPictureResolution;  
     }  
  
  private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {  
  final double ASPECT_TOLERANCE = 0.1;  
  double targetRatio = (double) w / h;  
  if (sizes == null)  
  return null;  
  
         Size optimalSize = null;  
  double minDiff = Double.MAX_VALUE;  
  
  int targetHeight = h;  
  
  // Try to find an size match aspect ratio and size 
  for (Size size : sizes) {  
  double ratio = (double) size.width / size.height;  
  if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)  
  continue;  
  if (Math.abs(size.height - targetHeight) < minDiff) {  
                 optimalSize = size;  
                 minDiff = Math.abs(size.height - targetHeight);  
             }  
         }  
  
  // Cannot find the one match the aspect ratio, ignore the requirement 
  if (optimalSize == null) {  
             minDiff = Double.MAX_VALUE;  
  for (Size size : sizes) {  
  if (Math.abs(size.height - targetHeight) < minDiff) {  
                     optimalSize = size;  
                     minDiff = Math.abs(size.height - targetHeight);  
                 }  
             }  
         }  
  return optimalSize;  
     }  
  
  
  public void reAutoFocus() {  
  if (isSupportAutoFocus) {  
             mCamera.autoFocus(new Camera.AutoFocusCallback() {  
  @Override 
  public void onAutoFocus(boolean success, Camera camera) {  
                 }  
             });  
         }  
     }  
  
  public List<Size> getResolutionList() {  
  return mCamera.getParameters().getSupportedPreviewSizes();  
     }  
  
  public Camera.Size getResolution() {  
         Camera.Parameters params = mCamera.getParameters();  
         Camera.Size s = params.getPreviewSize();  
  return s;  
     }  
  
  /*public void setCurrentCameraId(int current) { 
         mCurrentCameraId = current; 
     }*/ 
  
  //定点对焦的代码 
  public void pointFocus(MotionEvent event) {  
         mCamera.cancelAutoFocus();  
         parameters = mCamera.getParameters();  
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {  
  //showPoint(x, y); 
             focusOnTouch(event);  
         }  
         mCamera.setParameters(parameters);  
         autoFocus();  
     }  
  
  //实现自动对焦 
  public void autoFocus() {  
  new Thread() {  
  @Override 
  public void run() {  
  try {  
                     sleep(100);  
                 } catch (InterruptedException e) {  
                     e.printStackTrace();  
                 }  
  if (mCamera == null) {  
  return;  
                 }  
                 mCamera.autoFocus(new Camera.AutoFocusCallback() {  
  @Override 
  public void onAutoFocus(boolean success, Camera camera) {  
  if (success) {  
                             initCamera();//实现相机的参数初始化 
                         }  
                     }  
                 });  
             }  
         };  
     }  
  
  @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)  
  private void showPoint(int x, int y) {  
  if (parameters.getMaxNumMeteringAreas() > 0) {  
             List<Camera.Area> areas = new ArrayList<Camera.Area>();  
             WindowManager wm = (WindowManager) getContext()  
                     .getSystemService(Context.WINDOW_SERVICE);  
  //xy变换了 
  int rectY = -x * 2000 / wm.getDefaultDisplay().getWidth() + 1000;  
  int rectX = y * 2000 / wm.getDefaultDisplay().getHeight() - 1000;  
  int left = rectX < -900 ? -1000 : rectX - 100;  
  int top = rectY < -900 ? -1000 : rectY - 100;  
  int right = rectX > 900 ? 1000 : rectX + 100;  
  int bottom = rectY > 900 ? 1000 : rectY + 100;  
             Rect area1 = new Rect(left, top, right, bottom);  
             areas.add(new Camera.Area(area1, 800));  
             parameters.setMeteringAreas(areas);  
         }  
  
         parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);  
     }  
  
  @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)  
  public void focusOnTouch(MotionEvent event) {  
         Rect focusRect = calculateTapArea(event.getRawX(), event.getRawY(), 1f);  
         Rect meteringRect = calculateTapArea(event.getRawX(), event.getRawY(), 1.5f);  
  
         Camera.Parameters parameters = mCamera.getParameters();  
         parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);  
  
  if (parameters.getMaxNumFocusAreas() > 0) {  
             List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();  
             focusAreas.add(new Camera.Area(focusRect, 1000));  
  
             parameters.setFocusAreas(focusAreas);  
         }  
  
  if (parameters.getMaxNumMeteringAreas() > 0) {  
             List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();  
             meteringAreas.add(new Camera.Area(meteringRect, 1000));  
  
             parameters.setMeteringAreas(meteringAreas);  
         }  
         mCamera.setParameters(parameters);  
         mCamera.autoFocus(this);  
     }  
  
  /** 
      * Convert touch position x:y to {@link Camera.Area} position -1000:-1000 to 1000:1000. 
      */ 
  private Rect calculateTapArea(float x, float y, float coefficient) {  
  float focusAreaSize = 300;  
  int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();  
  
  int centerX = (int) (x / getResolution().width * 2000 - 1000);  
  int centerY = (int) (y / getResolution().height * 2000 - 1000);  
  
  int left = clamp(centerX - areaSize / 2, -1000, 1000);  
  int right = clamp(left + areaSize, -1000, 1000);  
  int top = clamp(centerY - areaSize / 2, -1000, 1000);  
  int bottom = clamp(top + areaSize, -1000, 1000);  
  
  return new Rect(left, top, right, bottom);  
     }  
  
  private int clamp(int x, int min, int max) {  
  if (x > max) {  
  return max;  
         }  
  if (x < min) {  
  return min;  
         }  
  return x;  
     }  
  
  @Override 
  public void onAutoFocus(boolean success, Camera camera) {  
  
     }  
  
  public void setNull() {  
         adapterSize = null;  
         mPreviewSize = null;  
     }  
  
 }  

以下是CameraActivity类:

 public class CameraActivity extends Activity implements View.OnTouchListener,OnClickListener {  
  
  public static final String CAMERA_PATH_VALUE1 = "PHOTO_PATH";  
  public static final String CAMERA_PATH_VALUE2 = "PATH";  
  public static final String CAMERA_TYPE = "CAMERA_TYPE";  
  public static final String CAMERA_RETURN_PATH = "return_path";  
  
  private int PHOTO_SIZE_W = 2000;  
  private int PHOTO_SIZE_H = 2000;  
  public static final int CAMERA_TYPE_1 = 1;  
  public static final int CAMERA_TYPE_2 = 2;  
  private final int PROCESS = 1;  
  private CameraPreview preview;  
  private Camera camera;  
  private Context mContext;  
  private View focusIndex;  
  private ImageView flashBtn;  
  private int mCurrentCameraId = 0; // 1是前置 0是后置 
  private SurfaceView mSurfaceView;  
  private CameraGrid mCameraGrid;  
  
  private int type = 1;   //引用的矩形框 
  
  private Button mBtnSearch;  
  private Button mBtnTakePhoto;  
  
  @Override 
  public void onCreate(Bundle savedInstanceState) {  
  super.onCreate(savedInstanceState);  
         mContext = this;  
  
  //requestWindowFeature(Window.FEATURE_NO_TITLE); 
  //getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏 
  //getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//拍照过程屏幕一直处于高亮 
         setContentView(R.layout.camera_home);  
         type = getIntent().getIntExtra(CAMERA_TYPE, CAMERA_TYPE_2);  
         initView();  
         InitData();  
  
     }  
  
  private void initView() {  
         focusIndex = (View) findViewById(R.id.focus_index);  
         flashBtn = (ImageView) findViewById(R.id.flash_view);  
         mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);  
         mCameraGrid = (CameraGrid) findViewById(R.id.camera_grid);  
         mBtnSearch = (Button) findViewById(R.id.search);  
         mBtnTakePhoto = (Button) findViewById(R.id.takephoto);  
     }  
  
  
  private void InitData() {  
         preview = new CameraPreview(this, mSurfaceView);  
         preview.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,  
                 LayoutParams.MATCH_PARENT));  
         ((FrameLayout) findViewById(R.id.layout)).addView(preview);  
         preview.setKeepScreenOn(true);  
         mSurfaceView.setOnTouchListener(this);  
         mCameraGrid.setType(type);  
     }  
  
  
  
  
  private Handler handler = new Handler();  
  
  private void takePhoto() {  
  try {  
  
             camera.takePicture(shutterCallback, rawCallback, jpegCallback);  
  
         } catch (Throwable t) {  
             t.printStackTrace();  
             Toast.makeText(getApplication(), "拍照失败,请重试!", Toast.LENGTH_LONG)  
             .show();  
  try {  
                 camera.startPreview();  
             } catch (Throwable e) {  
  
             }  
         }  
     }  
  
  
  
  @Override 
  protected void onResume() {  
  super.onResume();  
  int numCams = Camera.getNumberOfCameras();  
  if (numCams > 0) {  
  try {  
                 mCurrentCameraId = 0;  
                 camera = Camera.open(mCurrentCameraId);  
                 camera.startPreview();  
                 preview.setCamera(camera);  
                 preview.reAutoFocus();  
             } catch (RuntimeException ex) {  
                 Toast.makeText(mContext, "未发现相机", Toast.LENGTH_LONG).show();  
             }  
         }  
  
     }  
  
  
  
  @Override 
  protected void onPause() {  
  if (camera != null) {  
             camera.stopPreview();  
             preview.setCamera(null);  
             camera.release();  
             camera = null;  
             preview.setNull();  
         }  
  super.onPause();  
  
     }  
  
  
  private void resetCam() {  
         camera.startPreview();  
         preview.setCamera(camera);  
     }  
  
  
     ShutterCallback shutterCallback = new ShutterCallback() {  
  public void onShutter() {  
         }  
     };  
  
  
     PictureCallback rawCallback = new PictureCallback() {  
  public void onPictureTaken(byte[] data, Camera camera) {  
         }  
     };  
  
  
     PictureCallback jpegCallback = new PictureCallback() {  
  public void onPictureTaken(byte[] data, Camera camera) {  
  
  new SaveImageTask(data).execute();  
             resetCam();  
         }  
     };  
  
  
  @Override 
  public boolean onTouch(View v, MotionEvent event) {  
  try {  
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {  
                 preview.pointFocus(event);  
             }  
         } catch (Exception e) {  
             e.printStackTrace();  
         }  
  
         RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(  
                 focusIndex.getLayoutParams());  
         layout.setMargins((int) event.getX() - 60, (int) event.getY() - 60, 0,0);  
  
         focusIndex.setLayoutParams(layout);  
         focusIndex.setVisibility(View.VISIBLE);  
  
         ScaleAnimation sa = new ScaleAnimation(3f, 1f, 3f, 1f,  
                 ScaleAnimation.RELATIVE_TO_SELF, 0.5f,  
                 ScaleAnimation.RELATIVE_TO_SELF, 0.5f);  
         sa.setDuration(800);  
         focusIndex.startAnimation(sa);  
         handler.postAtTime(new Runnable() {  
  @Override 
  public void run() {  
                 focusIndex.setVisibility(View.INVISIBLE);  
             }  
         }, 800);  
  return false;  
     }  
  
  
  @Override 
  public void onClick(View v) {  
  switch (v.getId()) {  
  
  /*case R.id.camera_back: 
             setResult(0); 
             finish(); 
             break;*/ 
  
  case R.id.camera_flip_view:  
             switchCamera();  
  break;  
  
  case R.id.flash_view:  
             turnLight(camera);  
  break;  
  
  case R.id.action_button:  
             takePhoto();  
  break;  
  
  case R.id.search:   //处理选中状态 
             mBtnSearch.setSelected(true);  
             mBtnTakePhoto.setSelected(false);  
  break;  
  
  case R.id.takephoto:    //处理选中状态 
             mBtnTakePhoto.setSelected(true);  
             mBtnSearch.setSelected(false);  
  break;  
         }  
     }  
  
  private static String getCameraPath() {  
         Calendar calendar = Calendar.getInstance();  
         StringBuilder sb = new StringBuilder();  
         sb.append("IMG");  
         sb.append(calendar.get(Calendar.YEAR));  
  int month = calendar.get(Calendar.MONTH) + 1; // 0~11 
         sb.append(month < 10 ? "0" + month : month);  
  int day = calendar.get(Calendar.DATE);  
         sb.append(day < 10 ? "0" + day : day);  
  int hour = calendar.get(Calendar.HOUR_OF_DAY);  
         sb.append(hour < 10 ? "0" + hour : hour);  
  int minute = calendar.get(Calendar.MINUTE);  
         sb.append(minute < 10 ? "0" + minute : minute);  
  int second = calendar.get(Calendar.SECOND);  
         sb.append(second < 10 ? "0" + second : second);  
  if (!new File(sb.toString() + ".jpg").exists()) {  
  return sb.toString() + ".jpg";  
         }  
  
         StringBuilder tmpSb = new StringBuilder(sb);  
  int indexStart = sb.length();  
  for (int i = 1; i < Integer.MAX_VALUE; i++) {  
             tmpSb.append('(');  
             tmpSb.append(i);  
             tmpSb.append(')');  
             tmpSb.append(".jpg");  
  if (!new File(tmpSb.toString()).exists()) {  
  break;  
             }  
  
             tmpSb.delete(indexStart, tmpSb.length());  
         }  
  
  return tmpSb.toString();  
     }  
  
  
  
  //处理拍摄的照片 
  private class SaveImageTask extends AsyncTask<Void, Void, String> {  
  private byte[] data;  
  
         SaveImageTask(byte[] data) {  
  this.data = data;  
         }  
  
  @Override 
  protected String doInBackground(Void... params) {  
  // Write to SD Card 
             String path = "";  
  try {  
  
                 showProgressDialog("处理中");  
                 path = saveToSDCard(data);  
  
             } catch (FileNotFoundException e) {  
                 e.printStackTrace();  
             } catch (IOException e) {  
                 e.printStackTrace();  
             } finally {  
             }  
  return path;  
         }  
  
  
  @Override 
  protected void onPostExecute(String path) {  
  super.onPostExecute(path);  
  
  if (!TextUtils.isEmpty(path)) {  
  
                 Log.d("DemoLog", "path=" + path);  
  
                 dismissProgressDialog();  
                 Intent intent = new Intent();  
                 intent.setClass(CameraActivity.this, PhotoProcessActivity.class);  
                 intent.putExtra(CAMERA_PATH_VALUE1, path);  
                 startActivityForResult(intent, PROCESS);  
             } else {  
                 Toast.makeText(getApplication(), "拍照失败,请稍后重试!",  
                         Toast.LENGTH_LONG).show();  
             }  
         }  
     }  
  
  private AlertDialog mAlertDialog;  
  
  private void dismissProgressDialog() {  
  this.runOnUiThread(new Runnable() {  
  @Override 
  public void run() {  
  if (mAlertDialog != null && mAlertDialog.isShowing()  
                         && !CameraActivity.this.isFinishing()) {  
                     mAlertDialog.dismiss();  
                     mAlertDialog = null;  
                 }  
             }  
         });  
     }  
  
  private void showProgressDialog(final String msg) {  
  this.runOnUiThread(new Runnable() {  
  @Override 
  public void run() {  
  if (mAlertDialog == null) {  
                     mAlertDialog = new GenericProgressDialog(  
                             CameraActivity.this);  
                 }  
                 mAlertDialog.setMessage(msg);  
                 ((GenericProgressDialog) mAlertDialog)  
                 .setProgressVisiable(true);  
                 mAlertDialog.setCancelable(false);  
                 mAlertDialog.setOnCancelListener(null);  
                 mAlertDialog.show();  
                 mAlertDialog.setCanceledOnTouchOutside(false);  
             }  
         });  
     }  
  
  
  /** 
      * 将拍下来的照片存放在SD卡中 
      */ 
  public String saveToSDCard(byte[] data) throws IOException {  
         Bitmap croppedImage;  
  // 获得图片大小 
         BitmapFactory.Options options = new BitmapFactory.Options();  
         options.inJustDecodeBounds = true;  
         BitmapFactory.decodeByteArray(data, 0, data.length, options);  
  // PHOTO_SIZE = options.outHeight > options.outWidth ? options.outWidth 
  // : options.outHeight; 
         PHOTO_SIZE_W = options.outWidth;  
         PHOTO_SIZE_H = options.outHeight;  
         options.inJustDecodeBounds = false;  
         Rect r = new Rect(0, 0, PHOTO_SIZE_W, PHOTO_SIZE_H);  
  try {  
             croppedImage = decodeRegionCrop(data, r);  
         } catch (Exception e) {  
  return null;  
         }  
         String imagePath = "";  
  try {  
             imagePath = saveToFile(croppedImage);  
         } catch (Exception e) {  
  
         }  
         croppedImage.recycle();  
  return imagePath;  
     }  
  
  
  
  private Bitmap decodeRegionCrop(byte[] data, Rect rect) {  
         InputStream is = null;  
         System.gc();  
         Bitmap croppedImage = null;  
  try {  
             is = new ByteArrayInputStream(data);  
             BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is,false);  
  try {  
                 croppedImage = decoder.decodeRegion(rect,  
  new BitmapFactory.Options());  
             } catch (IllegalArgumentException e) {  
             }  
         } catch (Throwable e) {  
             e.printStackTrace();  
         } finally {  
  
         }  
         Matrix m = new Matrix();  
         m.setRotate(90, PHOTO_SIZE_W / 2, PHOTO_SIZE_H / 2);  
  if (mCurrentCameraId == 1) {  
             m.postScale(1, -1);  
         }  
         Bitmap rotatedImage = Bitmap.createBitmap(croppedImage, 0, 0,  
                 PHOTO_SIZE_W, PHOTO_SIZE_H, m, true);  
  if (rotatedImage != croppedImage)  
             croppedImage.recycle();  
  return rotatedImage;  
     }  
  
  
  
  // 保存图片文件 
  public static String saveToFile(Bitmap croppedImage)  
  throws FileNotFoundException, IOException {  
         File sdCard = Environment.getExternalStorageDirectory();  
         File dir = new File(sdCard.getAbsolutePath() + "/DCIM/Camera/");  
  if (!dir.exists()) {  
             dir.mkdirs();  
         }  
         String fileName = getCameraPath();  
         File outFile = new File(dir, fileName);  
         FileOutputStream outputStream = new FileOutputStream(outFile); // 文件输出流 
         croppedImage.compress(Bitmap.CompressFormat.JPEG, 70, outputStream);  
         outputStream.flush();  
         outputStream.close();  
  return outFile.getAbsolutePath();  
     }  
  
  
  /** 
      * 闪光灯开关 开->关->自动 
      * 
      * @param mCamera 
      */ 
  private void turnLight(Camera mCamera) {  
  if (mCamera == null || mCamera.getParameters() == null 
                 || mCamera.getParameters().getSupportedFlashModes() == null) {  
  return;  
         }  
         Camera.Parameters parameters = mCamera.getParameters();  
         String flashMode = mCamera.getParameters().getFlashMode();  
         List<String> supportedModes = mCamera.getParameters()  
                 .getSupportedFlashModes();  
  if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)  
                 && supportedModes.contains(Camera.Parameters.FLASH_MODE_ON)) {// 关闭状态 
             parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);  
             mCamera.setParameters(parameters);  
             flashBtn.setImageResource(R.drawable.camera_flash_on);  
         } else if (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {// 开启状态 
  if (supportedModes.contains(Camera.Parameters.FLASH_MODE_AUTO)) {  
                 parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);  
                 flashBtn.setImageResource(R.drawable.camera_flash_auto);  
                 mCamera.setParameters(parameters);  
             } else if (supportedModes  
                     .contains(Camera.Parameters.FLASH_MODE_OFF)) {  
                 parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);  
                 flashBtn.setImageResource(R.drawable.camera_flash_off);  
                 mCamera.setParameters(parameters);  
             }  
         } else if (Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)  
                 && supportedModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {  
             parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);  
             mCamera.setParameters(parameters);  
             flashBtn.setImageResource(R.drawable.camera_flash_off);  
         }  
     }  
  
  
  // 切换前后置摄像头 
  private void switchCamera() {  
         mCurrentCameraId = (mCurrentCameraId + 1) % Camera.getNumberOfCameras();  
  if (camera != null) {  
             camera.stopPreview();  
             preview.setCamera(null);  
             camera.setPreviewCallback(null);  
             camera.release();  
             camera = null;  
         }  
  try {  
             camera = Camera.open(mCurrentCameraId);  
             camera.setPreviewDisplay(mSurfaceView.getHolder());  
             preview.setCamera(camera);  
             camera.startPreview();  
         } catch (Exception e) {  
             Toast.makeText(mContext, "未发现相机", Toast.LENGTH_LONG).show();  
         }  
  
     }  
  
  @Override 
  public boolean onKeyDown(int keyCode, KeyEvent event) {  
  if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {  
             setResult(0);  
             finish();  
  return true;  
         }  
  return super.onKeyDown(keyCode, event);  
     }  
  
  
  @Override 
  public void onActivityResult(int requestCode, int resultCode, Intent data) {  
  if (requestCode == PROCESS) {  
  if (resultCode == RESULT_OK) {  
                 Intent intent = new Intent();  
  if (data != null) {  
                     intent.putExtra(CAMERA_RETURN_PATH,  
                             data.getStringExtra(CAMERA_PATH_VALUE2));  
                 }  
                 setResult(RESULT_OK, intent);  
                 finish();  
             } else {  
  if (data != null) {  
                     File dir = new File(data.getStringExtra(CAMERA_PATH_VALUE2));  
  if (dir != null) {  
                         dir.delete();  
                     }  
                 }  
             }  
         }  
     }  
 }  

总结

1、网上有些示例代码,担心相机初始化及开启时间较长,将初始化及启动工作单独放在子线程中,偶尔出现黑屏的情况,但也不是经常出现。

导致原因:由于单独开辟了线程去初始化启动相机,导致相机的初始化和开启工作已完成,而找不到画布控件。若出现此情况,可调试或者将线程睡眠500毫秒。

2、按下home键后,再次进入时,为毛黑屏了,如何破?

导致原因:在onCreate中find了SurfaceView,按下Home后程序再次进入时,找不到预览的画布了,可将find的工作放入onResume中,再就是别忘了在onPause中做如下操作:

 @Override 
  protected void onPause() {  
  if (camera != null) {  
             camera.stopPreview();  
             preview.setCamera(null);  
             camera.release();  
             camera = null;  
             preview.setNull();  
         }  
  super.onPause();  
  
     }  

本项目源码(Eclipse版):http://download.csdn.net/download/gao_chun/9084853

注:测试机-------> 小米2A、红米、华为P8、华为荣耀3C,魅蓝note2

附:有些小伙伴经常问手机Gif动画如何制作的,在此也分享下:

动画制作小软件GifMaker:http://download.csdn.net/detail/gao_chun/9077023

注:这代码有些问题,我会在后面贴上最新的优化代码,欢迎继续支持

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏五毛程序员

五毛的cocos2d-x学习笔记08-动画

1895
来自专栏Android干货园

一款基于Material Desgin设计的APP

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/50...

1931
来自专栏Java成神之路

Java微信公众平台开发_04_自定义菜单

自定义菜单中请求包的数据是Json字符串格式的,请参见:  Java_数据交换_fastJSON_01_用法入门

1383
来自专栏林冠宏的技术文章

如何独立开发一个网络请求框架

721
来自专栏菩提树下的杨过

Silverlight Telerik控件学习:GridView双向绑定

做过WinForm数据库开发的人,一定有类似经历:DataGrid绑定后,如果允许行编辑,数据一顿修改后,想批量保存修改后的结果,通常是将DataGrid的所有...

2255
来自专栏hotqin888的专栏

MeritMS+jQuery.Gantt价值管理系统增加项目进度展示

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

2441
来自专栏ascii0x03的安全笔记

IE的BHO通过IHTMLDocument2接口获得网页源代码

参考了凤之焚的专栏:http://blog.csdn.net/lion_wing/article/details/769742 但是他的源码有些问题,即IHTM...

3425
来自专栏叁金大数据

WPF播放器

最近由于工作需要,需要做一个播放软件,在网上参考了很多例子,园子里有很多代码。其中最多的就是wpf自带的MediaElement控件,或者VLC视频播放器。

3082
来自专栏LeoXu的博客

让 Android 的 WebView 支持 type 为 file 的 input,同时支持拍照

Android 的 WebView 组件默认是不启用 type 为 file 的 input 的,需要在代码中做一些类似 hack 的编码(因为解决问题的目标对...

1042
来自专栏C#

免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)

    在生活中有一种东西几乎已经快要成为我们的另一个电子”身份证“,那就是二维码。无论是在软件开发的过程中,还是在普通用户的日常中,几乎都离不开二维码。...

1.5K9

扫码关注云+社区

领取腾讯云代金券