一、前言
Android自定義相機開發中,常常會有通過手勢放大或縮小相機預覽畫面的需求,即數碼變焦DigitalZoom,
二、介面說明
1. 獲取最大的放大倍數
float maxZoom = mCameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
怎么理解這個值呢?
假設正常預覽畫面(即沒有縮放)矩形為 activity_rect,放大后的預覽畫面矩形為 crop_rect,那么它們寬高的比值最大就只能為 maxZoom,例如我測驗中獲取的該值為 10.0,(PS:activity_rect的寬高為分子,crop_rect的寬高是分母)
2. 獲取未縮放的正常預覽畫面大小
Rect rect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
這個大小要用來計算我們最終放大后顯示的畫面大小,
3. 相機預覽請求構造者設定顯示的畫面區域
mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomRect);
計算得到的 zoomRect,通過上述介面設定給預覽,
三、代碼實作
1. 相機View處理觸碰事件
private float mOldDistance;
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getPointerCount() == 2) { // 當觸碰點有2個時,才去放大縮小
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
// 點下時,得到兩個點間的距離為mOldDistance
mOldDistance = getFingerSpacing(event);
break;
case MotionEvent.ACTION_MOVE:
// 移動時,根據距離是變大還是變小,去放大還是縮小預覽畫面
float newDistance = getFingerSpacing(event);
if (newDistance > mOldDistance) {
mCameraProxy.handleZoom(true);
} else if (newDistance < mOldDistance) {
mCameraProxy.handleZoom(false);
}
// 更新mOldDistance
mOldDistance = newDistance;
break;
default:
break;
}
}
return super.onTouchEvent(event);
}
private static float getFingerSpacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
上面我們只處理了觸碰事件,預覽放大或縮小的邏輯由 mCameraProxy 的 handleZoom(boolean isZoomIn) 實作,
2. handleZoom() 實作
private int mZoom = 0; // 縮放
public void handleZoom(boolean isZoomIn) {
if (mCameraDevice == null || mCameraCharacteristics == null || mPreviewRequestBuilder == null) {
return;
}
float maxZoom = mCameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
Log.d(TAG, "handleZoom: maxZoom: " + maxZoom);
int factor = 100; // 放大/縮小的一個因素,設定越大越平滑,相應放大的速度也越慢
if (isZoomIn && mZoom < factor) {
mZoom++;
} else if (mZoom > 0) {
mZoom--;
}
// 解釋一下這個mZoom,它只是一個0~factor的值,我們把畫面從正常大小放大到最大這個程序,
// 劃分成100份(factor的值),即一份就是最小的像素變化單元,mZoom表示有多少個這樣的單元
Log.d(TAG, "handleZoom: mZoom: " + mZoom);
Rect rect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
// 計算最小像素變化單元minW和minH
int minW = (int) ((rect.width() - rect.width() / maxZoom) / (2 * factor));
int minH = (int) ((rect.height() - rect.height() / maxZoom) / (2 * factor));
// 裁剪的寬高像素就等于minW乘以mZoom了
int cropW = minW * mZoom;
int cropH = minH * mZoom;
Log.d(TAG, "handleZoom: cropW: " + cropW + ", cropH: " + cropH);
// 上下左右坐標分別減去對應的裁剪大小即得到放大顯示的區域了
Rect zoomRect = new Rect(rect.left + cropW, rect.top + cropH, rect.right - cropW, rect.bottom - cropH);
// 設定給預覽的RequestBuilder
mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomRect);
mPreviewRequest = mPreviewRequestBuilder.build();
startPreview(); // 需要重新 start preview 才能生效
}
再簡單說明一下上面的代碼,因為本身maxZoom的值并不會很大,如果直接使用 1~maxZoom 的 int 值去放大縮小,畫面變化就很劇烈,所以我加了一個 int factor = 100 去把這個程序劃分成了100份,這個值可以自己設定,所以 mZoom 的判定范圍也就變成了 0~factor,
再說計算 minW 和 minH 的代碼,原Rect的寬是 rect.width(),放大到最大時 zoomRect 的寬是 rect.width() / maxZoom,因為有左右兩邊,所以它們的差值需要除以2,然后劃分成 factor 份,需要再除以 factor,
最后,放大時我們讓 mZoom 自增1,縮小時讓 mZoom 自減1,根據 mZoom 就可以得到剪裁的寬高大大小了,
四、工程地址
完整的代碼可見:
https://github.com/afei-cn/CameraDemo/blob/master/app/src/main/java/com/afei/camerademo/camera/Camera2Proxy.java
相關使用實體可見:
自定義Camera系列之:SurfaceView + Camera2
自定義Camera系列之:TextureView + Camera2
自定義Camera系列之:GLSurfaceView + Camera2
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/293755.html
標籤:其他
上一篇:盤點:2021年大廠最實用全面的Android開發面試真題決議!
下一篇:H5頁面呼叫微信掃一掃
