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的宽高是分母)
Rect rect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
这个大小要用来计算我们最终放大后显示的画面大小。
3. 相机预览请求构造者设置显示的画面区域mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomRect);
计算得到的 zoomRect,通过上述接口设置给预览。
关于 SCALER_CROP_REGION ,详情可以参考:https://developer.android.com/reference/android/hardware/camera2/CaptureRequest#SCALER_CROP_REGION
三、代码实现 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)
实现。
private final int MAX_ZOOM = 100; // 放大的最大值,用于计算每次放大/缩小 *** 作改变的大小
private int mZoom = 0; // 缩放
private float mStepWidth; // 每次改变的宽度大小
private float mStepHeight; // 每次改变的高度大小
// 每次切换摄像头计算一次就行,结果缓存到成员变量中
private void initZoomParameter() {
Rect rect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Log.d(TAG, "sensor_info_active_array_size: " + rect);
// max_digital_zoom 表示 active_rect 除以 crop_rect 的最大值
float max_digital_zoom = mCameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
Log.d(TAG, "max_digital_zoom: " + max_digital_zoom);
// crop_rect的最小宽高
float minWidth = rect.width() / max_digital_zoom;
float minHeight = rect.height() / max_digital_zoom;
// 因为缩放时两边都要变化,所以要除以2
mStepWidth = (rect.width() - minWidth) / MAX_ZOOM / 2;
mStepHeight = (rect.height() - minHeight) / MAX_ZOOM / 2;
}
public void handleZoom(boolean isZoomIn) {
if (mCameraDevice == null || mCameraCharacteristics == null || mPreviewRequestBuilder == null) {
return;
}
if (isZoomIn && mZoom < MAX_ZOOM) { // 放大
mZoom++;
} else if (mZoom > 0) { // 缩小
mZoom--;
}
Log.v(TAG, "handleZoom: mZoom: " + mZoom);
Rect rect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
int cropW = (int) (mStepWidth * mZoom);
int cropH = (int) (mStepHeight * mZoom);
Rect zoomRect = new Rect(rect.left + cropW, rect.top + cropH, rect.right - cropW, rect.bottom - cropH);
Log.d(TAG, "zoomRect: " + zoomRect);
mPreviewRequestBuilder.set(CaptureRequest.SCALER_CROP_REGION, zoomRect);
startPreview(); // 需要重新 start preview 才能生效
}
再简单说明一下上面的代码,因为本身maxZoom的值并不会很大,如果直接使用 1~maxZoom 的 int 值去放大缩小,画面变化就很剧烈,所以设置了一个 MAX_ZOOM = 100
去把这个过程划分成了100份,这个值可以自己设定。所以 mZoom 的判定范围也就变成了 0~MAX_ZOOM 。
再说计算 mStepWidth 和 mStepHeight 的代码。原Rect的宽是 rect.width(),放大到最大时 zoomRect 的宽是 rect.width() / maxZoom,因为有左右两边,所以它们的差值需要除以2,然后划分成 MAX_ZOOM 份,需要再除以 MAX_ZOOM 。
最后,放大时我们让 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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)