- 移动开发
- 音视频
【免费下载链接】CameraView
📸 A well documented, high-level Android interface that makes capturing pictures and videos easy, addressing all of the common issues and needs. Real-time filters, gestures, watermarks, frame processing, RAW, output of any size.
在 CameraView 中,除了核心的拍照与录像 API,还有一组用于精细控制拍摄体验的"扩展控制":预览网格线(含黄金分割网格)、系统提示音开关、是否根据设备倾斜旋转输出媒体、UI 元素的方向同步回调,以及为照片和视频写入地理位置元数据。本文基于 CameraView 官方文档docs/_docs/more-features.md,结合cameraview模块源码,逐一讲解这些特性的 XML 属性、Java API、默认值、平台限制(如 API < 16、Camera1 引擎的特殊行为)以及底层实现链路,帮助你在相机应用中正确配置和定制这些功能。
一、扩展控制属性总览
以下 XML 展示了CameraView提供的一组"额外控制"属性,它们对应文档中Extra controls一节的全部内容:
<com.otaliastudios.cameraview.CameraView app:cameraPlaySounds="true|false" app:cameraGrid="off|draw3x3|draw4x4|drawPhi" app:cameraGridColor="@color/black" app:cameraAutoFocusResetDelay="0" app:cameraUseDeviceOrientation="true"/>这些属性在 attrs.xml 中以declare-styleable形式声明,其中cameraGrid是一个枚举属性,其取值与内部整数值映射关系为:
| XML 取值 | 整数值 | 对应枚举 |
|---|---|---|
off | 0 | Grid.OFF |
draw3x3 | 1 | Grid.DRAW_3X3 |
draw4x4 | 2 | Grid.DRAW_4X4 |
drawPhi | 3 | Grid.DRAW_PHI |
cameraPlaySounds与cameraUseDeviceOrientation均为boolean格式,cameraGridColor接受color|reference,cameraAutoFocusResetDelay接受integer|reference。XML 属性解析后最终会落到CameraView构造路径中的setPlaySounds/setGrid/setGridColor/setUseDeviceOrientation/setAutoFocusResetDelay调用上,因此 XML 配置与代码 API 是完全等价的两条入口。
二、cameraPlaySounds:系统提示音控制
cameraPlaySounds控制相机在特定事件(快门、对焦完成)时是否播放平台自带的提示音,默认值为true:
cameraView.setPlaySounds(true); cameraView.setPlaySounds(false);文档明确了两条平台限制:
- 在 API < 16 的设备上,该标志永远被强制为
false; - Camera1 引擎在拍照时总会播放快门声,与这个标志无关。
这两条限制在源码中可以直接得到印证。CameraView.java 的 setter 中,标志落地时就做了 API 级别裁剪:
public void setPlaySounds(boolean playSounds) { mPlaySounds = playSounds && Build.VERSION.SDK_INT >= 16; mCameraEngine.setPlaySounds(playSounds); }即本地MediaActionSound播放(playSound方法,CameraView.java)在低版本上直接失效,但引擎侧仍会收到该设置。播放时机在onDeviceOrientationChanged相邻的回调逻辑中可见:对焦成功且mPlaySounds为真时触发MediaActionSound.FOCUS_COMPLETE(CameraView.java)。
两条引擎的差异体现在各自的setPlaySounds实现中:
- Camera1:Camera1Engine.applyPlaySounds 会查询
CameraInfo.canDisableShutterSound,仅当设备允许关闭快门声时才调用mCamera.enableShutterSound(...);如果设备不允许关闭(canDisableShutterSound == false),代码会回滚mPlaySounds为旧值并返回false——这就是"Camera1 引擎总会播放快门声"这一说法的源码级解释。 - Camera2:Camera2Engine.setPlaySounds 仅记录
mPlaySounds状态,快门提示音由CameraView层用MediaActionSound统一播放。
三、cameraGrid:预览网格线
cameraGrid让你能够在预览上绘制构图辅助网格,支持的取值为off、draw3x3、draw4x4(常规网格)与drawPhi(基于黄金分割常数、摄影构图中常用的网格),默认为off:
cameraView.setGrid(Grid.OFF); cameraView.setGrid(Grid.DRAW_3X3); cameraView.setGrid(Grid.DRAW_4X4); cameraView.setGrid(Grid.DRAW_PHI);取值定义在 Grid.java,它实现Control接口,DEFAULT = OFF,并提供fromValue(int)反解析方法以支持 XML 枚举映射。
网格绘制原理
setGrid并不触碰相机,而是直接驱动一个覆盖在预览之上的专用视图——GridLinesLayout。CameraView.setGrid 的实现只有一行:
public void setGrid(@NonNull Grid gridMode) { mGridLinesLayout.setGridMode(gridMode); }GridLinesLayout内部的关键逻辑:
- 线数:
getLineCount()按模式返回OFF→0、DRAW_3X3→2、DRAW_PHI→2、DRAW_4X4→3,即在水平和垂直方向各画 N 条线; - 线位:常规网格使用等分公式
(1f / (lineCount + 1)) * (lineNumber + 1);DRAW_PHI模式使用黄金分割倒数常量GOLDEN_RATIO_INV = 0.61803398874989f,通过delta = 1f / (2 + GOLDEN_RATIO_INV)计算两条非对称线的位置(GridLinesLayout.java); - 渲染:
onDraw中用两个ColorDrawable分别平移绘制水平线与垂直线,线宽为 0.9dp(TypedValue.applyDimension(..., 0.9f, ...))。
这套行为的正确性由仪表测试 GridLinesLayoutTest.java 守护:testOff断言 0 条线、test3x3断言 2 条、testPhi断言 2 条、test4x4断言 3 条,通过DrawCallback.onDraw(lines)捕获实际绘制结果。
四、cameraGridColor:网格线颜色
cameraGridColor用于指定网格线的颜色,文档描述默认值是一种灰色调:
cameraView.setGridColor(Color.WHITE); cameraView.setGridColor(Color.BLACK);从源码看,默认颜色常量定义在 GridLinesLayout.java:
public final static int DEFAULT_COLOR = Color.argb(160, 255, 255, 255);即 alpha 为 160(约 63% 不透明度)的白色——叠加在相机预览画面之上时呈现为文档所说的"灰色调"。setGridColor会把颜色同时应用到水平和垂直两个ColorDrawable并postInvalidate()触发重绘(GridLinesLayout.java),因此颜色修改与网格模式切换一样是纯 UI 操作,即时生效、无需重新打开相机。CameraView侧还提供getGrid()/getGridColor()读取当前状态,且CameraView的通用setControl分发机制会将Grid类型的 Control 转发到setGrid(CameraView.java)。
五、cameraAutoFocusResetDelay:自动对焦重置延迟
XML 片段中的cameraAutoFocusResetDelay="0"对应 APIsetAutoFocusResetDelay(long),用于控制相机在一段时间未触发对焦后,自动将焦点重置为无穷远(无限对焦)的延迟毫秒数,默认0表示不自动重置:
cameraView.setAutoFocusResetDelay(3000); // 3 秒后重置焦点实现位于引擎层(CameraView.java 直接委托给mCameraEngine)。值得注意的是CameraView在切换引擎时会用setAutoFocusResetDelay(oldEngine.getAutoFocusResetDelay())显式迁移该状态(CameraView.java),说明它属于跨引擎保留的会话级设置。
六、cameraUseDeviceOrientation:设备方向与输出旋转
cameraUseDeviceOrientation控制拍照和录像输出是否考虑设备方向,默认true:
cameraView.setUseDeviceOrientation(true); // rotate media cameraView.setUseDeviceOrientation(false); // don't文档指出,可以将其设为false,以应对那些不希望输出媒体在拍摄瞬间随设备旋转而旋转的特殊场景(例如固定支架设备、嵌入式场景)。
源码揭示了它的一个巧妙实现:在onDeviceOrientationChanged回调中(CameraView.java),如果mUseDeviceOrientation == false,CameraView并不会忽略传感器数据,而是给引擎喂一个"伪造"的方向值:
if (!mUseDeviceOrientation) { // To fool the engine to return outputs in the VIEW reference system, // The device orientation should be set to -displayOffset. int fakeDeviceOrientation = (360 - displayOffset) % 360; mCameraEngine.getAngles().setDeviceOrientation(fakeDeviceOrientation); } else { mCameraEngine.getAngles().setDeviceOrientation(deviceOrientation); }从注释和源码结构看,这个fakeDeviceOrientation = -displayOffset的取法让引擎"以为"设备始终正对着当前视图方向,从而把输出锁定在 View 参考系下。另一个与方向系统相关的细节:当显示偏移(如分屏、折叠屏导致的窗口角度变化)改变时,onDisplayOffsetChanged会直接close()+open()重启相机(CameraView.java),因为方向参数无法在会话中热切换。
七、UI Orientation:让按钮与预览同步旋转
相机应用里,屏幕旋转被锁定为固定方向后,用户倾斜手持设备时,预览画面会"歪",此时通常希望快门按钮等 UI 元素跟着旋转。CameraView提供了onOrientationChanged回调,返回应施加给 View 的逆时针旋转角度,使其与用户当前握持方式及 Activity 原生方向保持一致:
cameraView.addCameraListener(new CameraListener() { @Override public void onOrientationChanged(int orientation) { // orientation is the counter-clockwise rotation that a View should have // based on current device tilting and native activity orientation. } });该回调定义在 CameraListener.java,Javadoc 明确参数为0、90、180、270之一,且标注@UiThread。其触发路径在CameraView内部回调中(CameraView.java):
final int value = (deviceOrientation + displayOffset) % 360; mUiHandler.post(new Runnable() { @Override public void run() { for (CameraListener listener : mListeners) { listener.onOrientationChanged(value); } } });即value = (设备方向 + 显示偏移) % 360,通过mUiHandler投递到 UI 线程后广播给所有已注册的CameraListener。典型用法是拿到该值后调用view.setRotation(orientation)让取景框外的按钮组与预览保持视觉一致。
八、Location APIs:照片 EXIF 与视频元数据
setLocation可以把地理位置标签写入照片的 EXIF(JPEG)和视频元数据。位置数据可以来自任何位置 Provider(需先取得相应定位权限)。文档特别强调:这不保证会被写入 snapshot(预览截图类输出)。
| Method | Description |
|---|---|
setLocation(Location) | Sets location data to be appended to picture/video metadata. |
setLocation(double, double) | Sets latitude and longitude to be appended to picture/video metadata. |
getLocation() | Retrieves location data previously applied with setLocation(). |
三个 API 在 CameraView.java 中实现,其中双参数重载内部构造一个 provider 名为"Unknown"的Location对象,时间戳取当前系统时间、海拔置 0,然后统一委托给mCameraEngine.setLocation(location):
public void setLocation(double latitude, double longitude) { Location location = new Location("Unknown"); location.setTime(System.currentTimeMillis()); location.setAltitude(0); location.setLatitude(latitude); location.setLongitude(longitude); mCameraEngine.setLocation(location); }底层写入链路
位置数据随拍摄任务被"钉"在结果 Stub 上:CameraBaseEngine 的takePicture/takePictureSnapshot/takeVideo/takeVideoSnapshot都会执行stub.location = mLocation。之后的分岔点:
- Camera2 高质量拍照:Camera2Engine 在构造
CaptureRequest时直接设置builder.set(CaptureRequest.JPEG_GPS_LOCATION, mLocation),由硬件管线把 GPS 信息写入 JPEG EXIF; - Camera1 高质量拍照:Camera1Engine 则调用
params.setGpsLatitude / setGpsLongitude / setGpsAltitude / setGpsTimestamp / setGpsProcessingMethod五个参数,provider 名会作为GpsProcessingMethod写入; - DNG 格式:Full2PictureRecorder 中
mDngCreator.setLocation(mResult.location),即 DNG 侧车文件也会带上位置; - 视频:FullVideoRecorder 在
prepareMediaRecorder阶段执行mMediaRecorder.setLocation((float) lat, (float) lon),坐标以float精度写入视频元数据。
这也解释了文档中"snapshot 不保证附加位置"的说明:snapshot 走的是预览帧捕获路径,引擎侧虽然同样填充了stub.location,但各快照录制器(如 GL 快照)并未像高质量拍照和视频那样消费该字段。
引擎重启时的位置迁移
setLocation在引擎层是一个"有状态任务"(scheduleStateful("location", ...)),会在相机打开/重启后重新应用。切换引擎时,CameraView同样会用旧引擎的getLocation()恢复新引擎的位置设置,保证跨引擎不丢失。
九、未单独成篇的能力:CameraUtils 与 CameraOptions
文档最后一节指出,部分功能与 API 未在主文档中展开,主要包括:
CameraUtils:CameraUtils.java 提供与具体CameraView实例无关的静态工具方法(如尺寸转换、预览尺寸计算等辅助能力);CameraOptions:CameraOptions.java 封装当前相机会话的全部可查询能力(支持的尺寸、闪光能力、最大缩放比等),并在onCameraOpened回调中交给使用者。
按文档建议,这两部分的细节请直接阅读对应源码或 Javadoc。
十、小结与验证要点
| 特性 | 默认值 | 生效层级 | 关键限制 |
|---|---|---|---|
cameraPlaySounds | true | MediaActionSound+ 引擎 | API < 16 恒为false;Camera1 快门声不可禁用时强制播放 |
cameraGrid | off | 纯 UI(GridLinesLayout) | 即时重绘,不重启相机 |
cameraGridColor | argb(160, 255, 255, 255) | 纯 UI | 半透明白色,预览上呈灰色 |
cameraAutoFocusResetDelay | 0(禁用) | 引擎层 | 切引擎时状态自动迁移 |
cameraUseDeviceOrientation | true | 引擎方向系统 | false时以-displayOffset锁定 View 参考系;显示偏移变化会重启相机 |
setLocation | 无 | EXIF / DNG / 视频元数据 | snapshot 不保证写入 |
这些"扩展控制"覆盖了相机应用中最常见的三类定制需求:构图辅助(网格)、拍摄反馈(提示音、方向同步的 UI 旋转)、以及媒体合规(地理元数据)。所有行为均可在本文引用的源码与 GridLinesLayoutTest 等测试用例中逐条核对,demo 应用(Option.kt 中的Grid、GridColor、UseDeviceOrientation选项)也提供了这些控制项的完整交互示例。
- 移动开发
- 音视频
【免费下载链接】CameraView
📸 A well documented, high-level Android interface that makes capturing pictures and videos easy, addressing all of the common issues and needs. Real-time filters, gestures, watermarks, frame processing, RAW, output of any size.
相关推荐
CameraView项目预览尺寸控制技术详解
CameraView项目预览尺寸控制技术详解 前言 在移动应用开发中,相机功能的实现往往需要处理复杂的预览尺寸适配问题。CameraView作为一个功能强大的相
移动开发音视频frontend-slides Mat 模板预览卡解析:材质触感演示设计系统的选择元数据与预览落地规则
frontend slides Mat 模板预览卡解析:材质触感演示设计系统的选择元数据与预览落地规则 Mat 是 frontend slides 项目 Bol
AI 技能AI 插件前端CameraView终极指南:如何完美处理Exif数据中的地理位置和方向信息 📸
CameraView终极指南:如何完美处理Exif数据中的地理位置和方向信息 📸 CameraView是一个功能强大的Android相机库,专门用于简化图片和
移动开发音视频
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考