HarmonyOS相机的基本使用实战 原创 精华
相机开发流程
相机权限申请
config.json
"reqPermissions": [
{"name": "ohos.permission.CAMERA" },
{ "name": "ohos.permission.WRITE_MEDIA"},
{"name": "ohos.permission.MICROPHONE"},
{ "name": "ohos.permission.MEDIA_LOCATION"}
]
MainAbility
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
requestPermissions();
}
private void requestPermissions() {
String[] permissions = {
SystemPermission.CAMERA,
SystemPermission.MICROPHONE,
SystemPermission.MEDIA_LOCATION,
SystemPermission.WRITE_MEDIA
};
List<String> permissionFiltered = Arrays.stream(permissions)
.filter(permission -> verifySelfPermission(permission) != IBundleManager.PERMISSION_GRANTED)
.collect(Collectors.toList());
requestPermissionsFromUser(permissionFiltered.toArray(new String[permissionFiltered.size()]), 0);
}
@Override
public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
if (permissions == null || permissions.length == 0 || grantResults == null || grantResults.length == 0) {
return;
}
for (int grantResult : grantResults) {
if (grantResult != IBundleManager.PERMISSION_GRANTED) {
terminateAbility();
break;
}
}
}
相机设备创建
布局文件上添加按钮:
<Button
ohos:id="$+id:create_camera_btn"
ohos:height="match_content"
ohos:width="300vp"
ohos:text="创建相机并配置"
ohos:text_size="20fp"
ohos:text_color="#ffffff"
ohos:background_element="#0000ff"
ohos:layout_alignment="horizontal_center"
ohos:top_padding="8vp"
ohos:bottom_padding="8vp"
ohos:left_padding="40vp"
ohos:right_padding="40vp"
ohos:top_margin="20vp"
/>
按钮的逻辑代码:
private void createCameraBtnFunc(Component component) {
//openCamera(); //先测试创建相机
initSurface(); //创建surface,里面包含的openCamera()
}
private void openCamera(){
System.out.println("createCameraBtnFunc start...");
CameraKit cameraKit = CameraKit.getInstance(getContext());
if(cameraKit == null){
System.out.println("cameraKit create error!");
}
System.out.println("cameraKit create success!"+cameraKit);
try {
// 获取当前设备的逻辑相机列表
String[] cameraIds = cameraKit.getCameraIds();
if (cameraIds.length <= 0) {
System.out.println("cameraIds size is 0");
}
System.out.println("逻辑相机ids:" + Arrays.toString(cameraIds));
//第一个参数cameraId
//第二和第三个参数负责相机创建和相机运行时的数据和状态检测
cameraKit.createCamera(cameraIds[0], new CameraStateCallbackImpl(), new EventHandler(EventRunner.create("CameraCb")));
} catch (IllegalStateException e) {
// 处理异常
}
}
private class CameraStateCallbackImpl extends CameraStateCallback {
@Override
public void onCreated(Camera camera) {
//创建相机成功的时候回调
}
@Override
public void onConfigured(Camera camera) {
}
}
至此,相机设备的创建已经完成,执行成功意味着相机系统的硬件已经完成了上电。创建相机设备成功后,在CameraStateCallback中会触发onCreated(Camera camera)回调,并且带回Camera对象,用于执行相机设备的操作。当一个新的相机设备成功创建后,首先需要对相机进行配置,调用configure(CameraConfig)方法实现配置。
相机设备配置
相机配置主要是设置预览、拍照、录像所需要用到的Surface(ohos.agp.graphics.Surface),没有配置过Surface,相应的功能不能使用。为了进行相机帧捕获结果的数据和状态检测,有时还需要在相机配置时调用setFrameStateCallback(FrameStateCallback, EventHandler)方法设置帧回调。相机配置成功后,在CameraStateCallback中会触发onConfigured(Camera camera)回调,然后才可以执行相机帧捕获相关的操作。
相机配置,布局文件上加一个布局:
<DirectionalLayout
ohos:id="$+id:surface_container"
ohos:height="400vp"
ohos:width="match_parent"/>
逻辑代码:
private SurfaceProvider surfaceProvider;
private DirectionalLayout surfaceContainer; //定义在最前面
private void initSurface() {
getWindow().setTransparent(true);
DirectionalLayout.LayoutConfig params = new DirectionalLayout.LayoutConfig(
ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT);
surfaceProvider = new SurfaceProvider(this);
surfaceProvider.setLayoutConfig(params);
surfaceProvider.pinToZTop(false);
surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack());
surfaceContainer.addComponent(surfaceProvider);
}
private class SurfaceCallBack implements SurfaceOps.Callback {
@Override
public void surfaceCreated(SurfaceOps callbackSurfaceOps) {
//surfaceCreated后可以创建相机对象, 按钮上点击后执行initSurface();
openCamera();
}
@Override
public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {
}
}
//在上面的CameraStateCallbackImpl的onCreated方法中添加代码
private Camera cameraDevice;
private Surface previewSurface;
private CameraConfig.Builder cameraConfigBuilder; //放前面
private final class CameraStateCallbackImpl extends CameraStateCallback {
@Override
public void onCreated(Camera camera) {
//创建相机成功的时候回调
cameraDevice = camera;
previewSurface = surfaceProvider.getSurfaceOps().get().getSurface();
cameraConfigBuilder = camera.getCameraConfigBuilder();
if (cameraConfigBuilder == null) {
System.out.println("onCreated cameraConfigBuilder is null");
return;
}
// 配置预览的Surface
cameraConfigBuilder.addSurface(previewSurface);
// 配置帧结果的回调
cameraConfigBuilder.setFrameStateCallback(frameStateCallbackImpl, new EventHandler(EventRunner.create()));
try {
// 相机设备配置
camera.configure(cameraConfigBuilder.build());
} catch (IllegalArgumentException e) {
System.out.println("Argument Exception");
} catch (IllegalStateException e) {
System.out.println("State Exception");
}
}
}
private FrameStateCallback frameStateCallbackImpl = new FrameStateCallback(){
//....更具需要实现接口里的方法
};
配置完成后,会回调public void onConfigured(Camera camera)方法,在这个方法里铺货相机拍摄的帧。
相机帧捕获
用户一般都是先看见预览画面才执行拍照或者其他功能,所以对于一个普通的相机应用,预览是必不可少的。通过getFrameConfigBuilder(FRAME_CONFIG_PREVIEW)方法获取预览配置模板,更多的帧配置项以及详细使用方法请参考API接口说明的FrameConfig.Builder部分。
通过triggerLoopingCapture(FrameConfig)方法实现循环帧捕获实现预览:
//在CameraStateCallbackImpl的onConfigured添加代码
private FrameConfig.Builder frameConfigBuilder;
private FrameConfig previewFrameConfig;
private final class CameraStateCallbackImpl extends CameraStateCallback {
@Override
public void onConfigured(Camera camera) {
//配置成功的时候回调
// 获取预览配置模板
frameConfigBuilder = camera.getFrameConfigBuilder(Camera.FrameConfigType.FRAME_CONFIG_PREVIEW);
// 配置预览Surface
frameConfigBuilder.addSurface(previewSurface);
previewFrameConfig = frameConfigBuilder.build();
try {
// 启动循环帧捕获
int triggerId = camera.triggerLoopingCapture(previewFrameConfig);
} catch (IllegalArgumentException e) {
System.out.println("Argument Exception");
} catch (IllegalStateException e) {
System.out.println("State Exception");
}
}
}
经过以上的操作,相机应用已经可以正常进行实时预览了。在预览状态下,开发者还可以执行其他操作,比如:当预览帧配置更改时,可以通过triggerLoopingCapture(FrameConfig)方法实现预览帧配置的更新;
<Button
ohos:id="$+id:change_camera_config_btn"
ohos:height="match_content"
ohos:width="300vp"
ohos:text="改变相机焦距配置"
ohos:text_size="20fp"
ohos:text_color="#ffffff"
ohos:background_element="#0000ff"
ohos:layout_alignment="horizontal_center"
ohos:top_padding="8vp"
ohos:bottom_padding="8vp"
ohos:left_padding="40vp"
ohos:right_padding="40vp"
ohos:top_margin="20vp"
/>
//改变相机焦距配置
private void changeCameraConfigBtnFunc(Component component) {
// 预览帧变焦值变更
frameConfigBuilder.setZoom(1.5f);
// 调用triggerLoopingCapture方法实现预览帧配置更新
cameraDevice.triggerLoopingCapture(frameConfigBuilder.build());
}
通过stopLoopingCapture()方法停止循环帧捕获(停止预览)。
<Button
ohos:id="$+id:stop_camera_preview_btn"
ohos:height="match_content"
ohos:width="300vp"
ohos:text="停止相机预览"
ohos:text_size="20fp"
ohos:text_color="#ffffff"
ohos:background_element="#0000ff"
ohos:layout_alignment="horizontal_center"
ohos:top_padding="8vp"
ohos:bottom_padding="8vp"
ohos:left_padding="40vp"
ohos:right_padding="40vp"
ohos:top_margin="20vp"
/>
//停止相机预览
private void stopCameraBtnFunc(Component component) {
// 停止预览帧捕获
cameraDevice.stopLoopingCapture();
}
到这里相机的基本用户流程,基本搞清楚,使用相机最后一步就是释放了。
<Button
ohos:id="$+id:release_camera_btn"
ohos:height="match_content"
ohos:width="300vp"
ohos:text="释放相机"
ohos:text_size="20fp"
ohos:text_color="#ffffff"
ohos:background_element="#0000ff"
ohos:layout_alignment="horizontal_center"
ohos:top_padding="8vp"
ohos:bottom_padding="8vp"
ohos:left_padding="40vp"
ohos:right_padding="40vp"
ohos:top_margin="20vp"
/>
private void releaseCameraBtnFunc(Component component) {
if (cameraDevice != null) {
// 关闭相机和释放资源
cameraDevice.release();
cameraDevice = null;
}
// 预览配置模板置空
previewFrameConfig = null;
}
最后的效果:
感谢钟洪发老师的分享。
这想必就是进阶课程的内容,先预习了。谢谢老师
划重点!
这算是相机最基本的用法,更复杂的这个操作是无法瞒足的,需要更高级的其他库去支持。
学习了
能不能把Demo源码地址发出来,比如gitee地址
上面就是源码啊,98%的代码都贴出来了!你按照步骤就能做出来!很小的细节自己补一下!
感谢钟老师的分享