Android自定義照相機,手動聚焦
阿新 • • 發佈:2019-02-16
專案總結:
專案需求:做一個相機頁面,以View形式呈現,可以新增到佈局檔案中,實現觸控聚焦
問題(難點):1.因為是要做成一個View,可以新增到佈局檔案中,所以這個View的大小不固定;因此預覽解析度無法確定,容易造成在橫豎屏切換後預覽影象變形
2.手動聚焦,需要計算出聚焦Area,這個area是定義在以預覽區域的中心為原點,左上角為(-1000,-1000),右下角(1000,1000)的座標系中;因此座標變 換是一個問題
如何獲得相機例項並顯示到螢幕上,網上很多例子,不做過多說明,程式碼如下:
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; }
第二個問題的解決方法,根據點在螢幕上的座標,計算出在顯示區域的座標
原始碼地址:http://download.csdn.net/detail/minyou_1314/7854309 或者到我的資源中下載/** * 設定焦點和測光區域 * @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>