IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Camera2相机预览画面放大缩小(数码变焦DigitalZoom)功能实现 -> 正文阅读

[移动开发]Android Camera2相机预览画面放大缩小(数码变焦DigitalZoom)功能实现

一、前言

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

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-14 14:11:29  更:2021-08-14 14:12:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 21:38:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码