前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android 自定义相机

android 自定义相机

作者头像
xiangzhihong
发布2018-02-01 16:28:33
2.8K0
发布2018-02-01 16:28:33
举报
文章被收录于专栏:向治洪向治洪

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

方式:

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

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

代码语言:java
复制
 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权限,例如:

代码语言:html
复制
 <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的示例相机模块,此功能模块并非上面开源项目中的剥离出来的,看下效果图咯:

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

代码语言:html
复制
 <?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 类:

代码语言:java
复制
 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类:

代码语言:java
复制
 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中做如下操作:

代码语言:java
复制
 @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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-04-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 相机开发简介
  • 本程序模块效果图及示例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档