Android自定义照相机,手动聚焦

项目总结:

     项目需求:做一个相机页面,以View形式呈现,可以添加到布局文件中,实现触摸聚焦

     问题(难点):1.因为是要做成一个View,可以添加到布局文件中,所以这个View的大小不固定;因此预览分辨率无法确定,容易造成在横竖屏切换后预览图像变形

                                 2.手动聚焦,需要计算出聚焦Area,这个area是定义在以预览区域的中心为原点,左上角为(-1000,-1000),右下角(1000,1000)的坐标系中;因此坐标变 换是一个问题

    代码demo我已上传

    如何获得相机实例并显示到屏幕上,网上很多例子,不做过多说明,代码如下:

 private Camera getCameraInstance() {
    Camera c = null;
    try {
      int cameraCount = 0;

      Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
      cameraCount = Camera.getNumberOfCameras(); // get cameras number

      for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
        Camera.getCameraInfo(camIdx, cameraInfo); // get camerainfo
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { // 代表摄像头的方位,目前有定义值两个分别为CAMERA_FACING_FRONT前置和CAMERA_FACING_BACK后置
          try {
            c = Camera.open(camIdx);
          } catch (RuntimeException e) {
          }
        }
      }
      if (c == null) {
        c = Camera.open(0); // attempt to get a Camera instance
      }
    } catch (Exception e) {
      // Toast.makeText(context, "摄像头打开失败!", Toast.LENGTH_SHORT);
    }
    return c;
  }

    第一问题解决方法,获得显示区域的大小,与手机相机支持的预览分辨率对比,取得与显示区域宽高比最接近的最大手机预览分辨率,根据获得预览分辨率值,更改显示区域的宽高。取手机预览分辨率的最大值是为了让预览更清晰
  private void updateCameraParameters() {
    if (camera != null) {
      Camera.Parameters p = camera.getParameters();

      long time = new Date().getTime();
      p.setGpsTimestamp(time);

      Size previewSize = findBestPreviewSize(p);
      p.setPreviewSize(previewSize.width, previewSize.height);
      //这里设置图片的分辨率为预览图像的分辨率大小,这样拍照后的照片就与看到的一样(主要是清晰度),实际上可以根据需求自定定义
      p.setPictureSize(previewSize.width, previewSize.height);

      // Set the preview frame aspect ratio according to the picture size.

      frameLayout.setAspectRatio((double) previewSize.width / previewSize.height);

      if (context.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
        camera.setDisplayOrientation(90);
        p.setRotation(90);
      }
      camera.setParameters(p);
    }
  }

  /**
   * 找到最合适的显示分辨率 (防止预览图像变形)
   * @param parameters
   * @return
   */
  private Size findBestPreviewSize(Camera.Parameters parameters) {

    //系统支持的所有预览分辨率
    String previewSizeValueString = null;
    previewSizeValueString = parameters.get("preview-size-values");

    if (previewSizeValueString == null) {
      previewSizeValueString = parameters.get("preview-size-value");
    }

    if (previewSizeValueString == null) { // 有些手机例如m9获取不到支持的预览大小 就直接返回屏幕大小
      return camera.new Size(getScreenWH().widthPixels, getScreenWH().heightPixels);
    }
    float bestX = 0;
    float bestY = 0;

    float tmpRadio = 0;
    float viewRadio = 0;

    if (viewWidth != 0 && viewHeight != 0) {
      viewRadio = Math.min((float) viewWidth, (float) viewHeight) / Math.max((float) viewWidth, (float) viewHeight);
    }
    // System.out.println("CustomCameraView previewSizeValueString COMMA_PATTERN = "
    // + previewSizeValueString);

    String[] COMMA_PATTERN = previewSizeValueString.split(",");
    for (String prewsizeString : COMMA_PATTERN) {
      prewsizeString = prewsizeString.trim();

      int dimPosition = prewsizeString.indexOf('x');
      if (dimPosition == -1) {
        continue;
      }

      float newX = 0;
      float newY = 0;

      try {
        newX = Float.parseFloat(prewsizeString.substring(0, dimPosition));
        newY = Float.parseFloat(prewsizeString.substring(dimPosition + 1));
      } catch (NumberFormatException e) {
        continue;
      }

      float radio = Math.min(newX, newY) / Math.max(newX, newY);
      if (tmpRadio == 0) {
        tmpRadio = radio;
        bestX = newX;
        bestY = newY;
      } else if (tmpRadio != 0 && (Math.abs(radio - viewRadio)) < (Math.abs(tmpRadio - viewRadio))) {
        tmpRadio = radio;
        bestX = newX;
        bestY = newY;
      }
    }

    if (bestX > 0 && bestY > 0) {
      // System.out.println("CustomCameraView previewSizeValueString bestX = " +
      // bestX + ", bestY = " + bestY);
      return camera.new Size((int) bestX, (int) bestY);
    }
    return null;
  }

第二个问题的解决方法,根据点在屏幕上的坐标,计算出在显示区域的坐标
  /**
   * 设置焦点和测光区域
   * @param event 触摸点
   */
  public void focusOnTouch(MotionEvent event) {

    int[] location = new int[2];
    frameLayout.getLocationOnScreen(location);

    Rect focusRect = calculateTapArea(view_focus.getWidth(), view_focus.getHeight(), 1f, event.getRawX(), event.getRawY(),
        location[0], location[0] + frameLayout.getWidth(), location[1], location[1] + frameLayout.getHeight());
    Rect meteringRect = calculateTapArea(view_focus.getWidth(), view_focus.getHeight(), 1.5f, event.getRawX(), event.getRawY(),
        location[0], location[0] + frameLayout.getWidth(), location[1], location[1] + frameLayout.getHeight());

    Camera.Parameters parameters = camera.getParameters();
    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

    // System.out.println("CustomCameraView getMaxNumFocusAreas = " +
    // parameters.getMaxNumFocusAreas());
    if (parameters.getMaxNumFocusAreas() > 0) {
      List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
      focusAreas.add(new Camera.Area(focusRect, 1000));

      parameters.setFocusAreas(focusAreas);
    }

    // System.out.println("CustomCameraView getMaxNumMeteringAreas = " +
    // parameters.getMaxNumMeteringAreas());
    if (parameters.getMaxNumMeteringAreas() > 0) {
      List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
      meteringAreas.add(new Camera.Area(meteringRect, 1000));

      parameters.setMeteringAreas(meteringAreas);
    }

    try {
      camera.setParameters(parameters);
    } catch (Exception e) {
    }
    camera.autoFocus(this);
  }

  /**
   * 计算焦点及测光区域
   * @param focusWidth
   * @param focusHeight
   * @param areaMultiple
   * @param x
   * @param y
   * @param previewleft
   * @param previewRight
   * @param previewTop
   * @param previewBottom
   * @return Rect(left,top,right,bottom) :  left、top、right、bottom是以显示区域中心为原点的坐标 。   Rect 是以触摸点为中心的一块区域
   */
  public Rect calculateTapArea(int focusWidth, int focusHeight, float areaMultiple,
      float x, float y, int previewleft, int previewRight, int previewTop, int previewBottom) {
    int areaWidth = (int) (focusWidth * areaMultiple);
    int areaHeight = (int) (focusHeight * areaMultiple);
    int centerX = (previewleft + previewRight) / 2;
    int centerY = (previewTop + previewBottom) / 2;
    double unitx = ((double) previewRight - (double) previewleft) / 2000;
    double unity = ((double) previewBottom - (double) previewTop) / 2000;
    int left = clamp((int) (((x - areaWidth / 2) - centerX) / unitx), -1000, 1000);
    int top = clamp((int) (((y - areaHeight / 2) - centerY) / unity), -1000, 1000);
    int right = clamp((int) (left + areaWidth / unitx), -1000, 1000);
    int bottom = clamp((int) (top + areaHeight / unity), -1000, 1000);

    return new Rect(left, top, right, bottom);
  }
  public int clamp(int x, int min, int max) {
    if (x > max)
      return max;
    if (x < min)
      return min;
    return x;
  }<a target=_blank href="http://http://download.csdn.net/detail/minyou_1314/7854309">
</a>
源码地址:http://download.csdn.net/detail/minyou_1314/7854309  或者到我的资源中下载

   

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值