<SurfaceProvider
ohos:id="$+id:sp"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:weight="1"
/>
在config.json中申请相机权限:
"reqPermissions": [ { "name":"ohos.permission.CAMERA" } ]
在MainAbility中检查是否已获得相机权限,若未获得且可以获得,则动态申请该权限:
String permission = "ohos.permission.CAMERA"; if (verifySelfPermission(permission) == -1 && canRequestPermission(permission)) { List<String> permissions = new ArrayList<>(); permissions.add(permission); requestPermissionsFromUser( permissions.toArray(new String[0]), 1001); }
在该权限的用户响应回调中检查用户是否允许,若允许则调用MainAbilitySlice的initCamera()方法,初始化相机:
public void onRequestPermissionsFromUserResult( int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsFromUserResult( requestCode, permissions, grantResults); if (requestCode == 1001 && grantResults.length > 0 && grantResults[0] == IBundleManager.PERMISSION_GRANTED) // 初始化相机 mainAbilitySlice.initCamera(); }
在MainAbilitySlice中检查是否已获得相机权限,若已获得则直接调用initCamera()方法,初始化相机,否则显示拒绝页面,并将动态申请该权限的工作交给MainAbility:
if (verifySelfPermission("ohos.permission.CAMERA") == 0) // 初始化相机 initCamera(); else { super.setUIContent(ResourceTable.Layout_ability_deny); ((MainAbility)getAbility()).setMainAbilitySlice(this); }
// 实时预览Surface对象 private Surface surface; // 相机事件处理器对象 private EventHandler eventHandler; // 相机对象 private Camera camera; // 照片接收器对象 private ImageReceiver imageReceiver = ImageReceiver.create( 4096, 3072, ImageFormat.JPEG, 5); // 相机状态回调对象 private CameraStateCallback cameraStateCallback = new CameraStateCallback() { // 创建相机对象成功 @Override public void onCreated(Camera c) { // 得到相机对象 super.onCreated(camera = c); // 配置相机对象 CameraConfig.Builder builder = camera.getCameraConfigBuilder(); if (builder != null) { // 添加实时预览Surface对象 builder.addSurface(surface); // 添加照片显示Surface对象 builder.addSurface( imageReceiver.getRecevingSurface()); // 开始配置 camera.configure(builder.build()); } } // 创建相机对象失败 @Override public void onCreateFailed(String cameraId, int errorCode) { super.onCreateFailed(cameraId, errorCode); HiLog.info(label, "Unable to create camera: %{public}d", errorCode); } // 配置相机对象成功 @Override public void onConfigured(Camera camera) { super.onConfigured(camera); // 配置实时预览帧 FrameConfig.Builder builder = camera.getFrameConfigBuilder( FrameConfigType.FRAME_CONFIG_PREVIEW); // 添加实时预览Surface对象 builder.addSurface(surface); // 开始循环帧捕获 camera.triggerLoopingCapture(builder.build()); } // 配置相机对象失败 @Override public void onConfigureFailed(Camera camera, int errorCode) { super.onConfigureFailed(camera, errorCode); HiLog.info(label, "Unable to configure camera: %{public}d", errorCode); } };
// 初始化相机 public void initCamera() { super.setUIContent(ResourceTable.Layout_ability_main); // 为照片接收器对象设置侦听 imageReceiver.setImageArrivalListener((ImageReceiver ir) -> { // 照片文件名 String filename = "IMG_" + new SimpleDateFormat( "yyyyMMdd_HHmmss").format(System.currentTimeMillis()) + ".jpg"; // 照片文件对象 final File file = new File(getExternalFilesDir("Camera"), filename); // 读取照片数据 final Image image = ir.readNextImage(); // 异步方式保存照片数据 eventHandler.postTask(() -> { // 通过Component对象读取照片字节序列 Image.Component component = image.getComponent(ComponentType.JPEG); byte[] bytes = new byte[component.remaining()]; component.read(bytes); // 通过输出流保存数据 FileOutputStream fos = null; try { fos = new FileOutputStream(file); fos.write(bytes); // 写照片数据 HiLog.info(label, file.getAbsolutePath()); getUITaskDispatcher().asyncDispatch( () -> showToast("拍照成功")); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } finally { // 关闭输出流 if (fos != null) try { fos.close(); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } // 释放照片数据 image.release(); } }); }); // 画面预览SurfaceProvider对象 SurfaceProvider sp = (SurfaceProvider) findComponentById( ResourceTable.Id_sp); sp.pinToZTop(true); sp.getSurfaceOps().get().addCallback(new Callback() { @Override public void surfaceCreated(SurfaceOps surfaceOps) { // 获取实时预览Surface对象 surface = surfaceOps.getSurface(); // 创建相机对象 createCamera(); } @Override public void surfaceChanged(SurfaceOps surfaceOps, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceOps surfaceOps) { } }); findComponentById(ResourceTable.Id_btn) .setClickedListener(component -> onClick()); }
// 创建相机对象 private boolean createCamera() { // 获取相机管理器对象 CameraKit cameraKit = CameraKit.getInstance(this); if (cameraKit == null) { HiLog.info(label, "Unable to get CameraKit"); return false; } // 获取逻辑相机列表 String[] cameraIds = cameraKit.getCameraIds(); if (cameraIds == null || cameraIds.length <= 0) { HiLog.info(label, "Unable to get CameraIds"); return false; } // 创建相机事件处理器对象 eventHandler = new EventHandler(EventRunner.current()); // 创建相机对象 cameraKit.createCamera( cameraIds[0], cameraStateCallback, eventHandler); return true; }
// 拍照 private void onClick() { // 配置照片显示帧 FrameConfig.Builder builder = camera.getFrameConfigBuilder( FrameConfigType.FRAME_CONFIG_PICTURE); // 添加照片显示Surface对象 builder.addSurface(imageReceiver.getRecevingSurface()); // 照片旋转90度 builder.setImageRotation(90); try { // 单帧捕获 camera.triggerSingleCapture(builder.build()); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } }
例程:Photograph
...\Photograph\entry\src\main\config.json
{ ... "module": { ... "reqPermissions": [ { "name":"ohos.permission.CAMERA" } ] } }
...\Photograph\entry\src\main\resources\base\media\camera.jpg
...\Photograph\entry\src\main\resources\base\layout\toast_dialog.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_content" ohos:width="match_content" ohos:left_padding="10vp" ohos:top_padding="5vp" ohos:right_padding="10vp" ohos:bottom_padding="5vp" ohos:background_element="#404040"> <Text ohos:id="$+id:txt" ohos:height="match_content" ohos:width="match_content" ohos:text_size="20fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\Photograph\entry\src\main\resources\base\layout\ability_deny.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:alignment="center" ohos:orientation="vertical" ohos:background_element="#000000"> <Image ohos:height="match_content" ohos:width="match_content" ohos:image_src="$media:camera" /> </DirectionalLayout>
...\Photograph\entry\src\main\resources\base\layout\ability_main.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:alignment="center" ohos:orientation="vertical"> <SurfaceProvider ohos:id="$+id:sp" ohos:height="match_parent" ohos:width="match_parent" ohos:weight="1" /> <Button ohos:id="$+id:btn" ohos:height="match_content" ohos:width="match_parent" ohos:padding="$ohos:float:button_radius" ohos:background_element="#00a2e8" ohos:text="拍照" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\Photograph\entry\src\main\java\com\minwei\photograph\MainAbility.java
public class MainAbility extends Ability { private MainAbilitySlice mainAbilitySlice; public void setMainAbilitySlice(MainAbilitySlice slice) { mainAbilitySlice = slice; } @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(MainAbilitySlice.class.getName()); requestCameraPermission(); } private void requestCameraPermission() { String permission = "ohos.permission.CAMERA"; if (verifySelfPermission(permission) == -1 && canRequestPermission(permission)) { List<String> permissions = new ArrayList<>(); permissions.add(permission); requestPermissionsFromUser( permissions.toArray(new String[0]), 1001); } } @Override public void onRequestPermissionsFromUserResult( int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsFromUserResult( requestCode, permissions, grantResults); if (requestCode == 1001 && grantResults.length > 0 && grantResults[0] == IBundleManager.PERMISSION_GRANTED) // 初始化相机 mainAbilitySlice.initCamera(); } }
...\Photograph\entry\src\main\java\com\minwei\photograph\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); // 实时预览Surface对象 private Surface surface; // 相机事件处理器对象 private EventHandler eventHandler; // 相机对象 private Camera camera; // 照片接收器对象 private ImageReceiver imageReceiver = ImageReceiver.create( 4096, 3072, ImageFormat.JPEG, 5); // 相机状态回调对象 private CameraStateCallback cameraStateCallback = new CameraStateCallback() { // 创建相机对象成功 @Override public void onCreated(Camera c) { // 得到相机对象 super.onCreated(camera = c); // 配置相机对象 CameraConfig.Builder builder = camera.getCameraConfigBuilder(); if (builder != null) { // 添加实时预览Surface对象 builder.addSurface(surface); // 添加照片显示Surface对象 builder.addSurface( imageReceiver.getRecevingSurface()); // 开始配置 camera.configure(builder.build()); } } // 创建相机对象失败 @Override public void onCreateFailed(String cameraId, int errorCode) { super.onCreateFailed(cameraId, errorCode); HiLog.info(label, "Unable to create camera: %{public}d", errorCode); } // 配置相机对象成功 @Override public void onConfigured(Camera camera) { super.onConfigured(camera); // 配置实时预览帧 FrameConfig.Builder builder = camera.getFrameConfigBuilder( FrameConfigType.FRAME_CONFIG_PREVIEW); // 添加实时预览Surface对象 builder.addSurface(surface); // 开始循环帧捕获 camera.triggerLoopingCapture(builder.build()); } // 配置相机对象失败 @Override public void onConfigureFailed(Camera camera, int errorCode) { super.onConfigureFailed(camera, errorCode); HiLog.info(label, "Unable to configure camera: %{public}d", errorCode); } }; @Override public void onStart(Intent intent) { super.onStart(intent); if (verifySelfPermission("ohos.permission.CAMERA") == 0) // 初始化相机 initCamera(); else { super.setUIContent(ResourceTable.Layout_ability_deny); ((MainAbility)getAbility()).setMainAbilitySlice(this); } } ... // 初始化相机 public void initCamera() { super.setUIContent(ResourceTable.Layout_ability_main); // 为照片接收器对象设置侦听 imageReceiver.setImageArrivalListener((ImageReceiver ir) -> { // 照片文件名 String filename = "IMG_" + new SimpleDateFormat( "yyyyMMdd_HHmmss").format(System.currentTimeMillis()) + ".jpg"; // 照片文件对象 final File file = new File(getExternalFilesDir("Camera"), filename); // 读取照片数据 final Image image = ir.readNextImage(); // 异步方式保存照片数据 eventHandler.postTask(() -> { // 通过Component对象读取照片字节序列 Image.Component component = image.getComponent(ComponentType.JPEG); byte[] bytes = new byte[component.remaining()]; component.read(bytes); // 通过输出流保存数据 FileOutputStream fos = null; try { fos = new FileOutputStream(file); fos.write(bytes); // 写照片数据 HiLog.info(label, file.getAbsolutePath()); getUITaskDispatcher().asyncDispatch( () -> showToast("拍照成功")); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } finally { // 关闭输出流 if (fos != null) try { fos.close(); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } // 释放照片数据 image.release(); } }); }); // 画面预览SurfaceProvider对象 SurfaceProvider sp = (SurfaceProvider) findComponentById( ResourceTable.Id_sp); sp.pinToZTop(true); sp.getSurfaceOps().get().addCallback(new Callback() { @Override public void surfaceCreated(SurfaceOps surfaceOps) { // 获取实时预览Surface对象 surface = surfaceOps.getSurface(); // 创建相机对象 createCamera(); } @Override public void surfaceChanged(SurfaceOps surfaceOps, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceOps surfaceOps) { } }); findComponentById(ResourceTable.Id_btn) .setClickedListener(component -> onClick()); } private void showToast(String text) { Component component = LayoutScatter.getInstance(this).parse( ResourceTable.Layout_toast_dialog, null, false); ((Text)component.findComponentById(ResourceTable.Id_txt)) .setText(text); new ToastDialog(this) .setContentCustomComponent(component) .setSize(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT) .setDuration(5000) .setAlignment(LayoutAlignment.BOTTOM) .setOffset(0, AttrHelper.vp2px(60, this)) .show(); } // 创建相机对象 private boolean createCamera() { // 获取相机管理器对象 CameraKit cameraKit = CameraKit.getInstance(this); if (cameraKit == null) { HiLog.info(label, "Unable to get CameraKit"); return false; } // 获取逻辑相机列表 String[] cameraIds = cameraKit.getCameraIds(); if (cameraIds == null || cameraIds.length <= 0) { HiLog.info(label, "Unable to get CameraIds"); return false; } // 创建相机事件处理器对象 eventHandler = new EventHandler(EventRunner.current()); // 创建相机对象 cameraKit.createCamera( cameraIds[0], cameraStateCallback, eventHandler); return true; } // 拍照 private void onClick() { // 配置照片显示帧 FrameConfig.Builder builder = camera.getFrameConfigBuilder( FrameConfigType.FRAME_CONFIG_PICTURE); // 添加照片显示Surface对象 builder.addSurface(imageReceiver.getRecevingSurface()); // 照片旋转90度 builder.setImageRotation(90); try { // 单帧捕获 camera.triggerSingleCapture(builder.build()); } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } } }
运行效果如下图所示:
在鸿蒙设备中用于应用程序存储文件的位置有两个:
/data/user/0/<bundle_name>/ - 沙盒目录,getDataDir()
|
|_ cache/ - 缓存目录,getCacheDir()
|_ code_cache/ - 代码缓存,getCodeCacheDir()
|_ files/ - 文件目录,getFilesDir()
/storage/emulated/0/ \
或 | - 外部存储
/storage/emulated/sdcard/ /
|
|_ Android/data/<bundle_name>/ - 私有目录,用户能访问,其它应用不能访问
| |
| |_ cache/ - 缓存目录,getExternalCacheDir()
| |_ <name1>/ - 自建目录,getExternalFilesDir(<name1>)
| |_ <name2>/ - 自建目录,getExternalFilesDir(<name2>)
| |_ ... - ...
|
|_ ... - 公有区域,用户和其它应用都能访问
应用程序访问沙盒目录和外部存储私有目录中的文件,不需要任何特殊权限,直接
使用Java语言提供的文件操作接口即可。但如果需要访问外部存储公有区域中的文
件,则必须借助于DataAbility。
鸿蒙系统在外部存储的公有区域中,为图像、视频、音频等多媒体文件划定了专门
的存储目录。借助DataAbilityHelper和相应的URI常量,可以很方便地访问这些目
录中的文件。
DataAbilityHelper helper = DataAbilityHelper.creator(this); ResultSet result = null; try { result = helper.query(Images.Media.EXTERNAL_DATA_ABILITY_URI, new String[]{Images.Media.ID}, null); HiLog.info(label, "图像文件数量:%{public}d", result.getRowCount()); HiLog.info(label, "图像文件列表:"); while (result.goToNextRow()) { int id = result.getInt(0); Uri uri = Uri.appendEncodedPathToUri( Images.Media.EXTERNAL_DATA_ABILITY_URI, String.valueOf(id)); HiLog.info(label, uri.toString()); /* FileDescriptor fd = helper.openFile(uri, "r"); FileReader fr = new FileReader(fd); // ... */ } } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } finally { if (result != null) result.close(); }
例程:AVStorage
...\AVStorage\entry\src\main\resources\base\graphic\background_button.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="100"/> <solid ohos:color="#ff7f27"/> </shape>
...\AVStorage\entry\src\main\resources\base\layout\ability_main.xml
<?xml version="1.0" encoding="utf-8"?> <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:height="match_parent" ohos:width="match_parent" ohos:left_padding="80vp" ohos:right_padding="80vp" ohos:alignment="center" ohos:orientation="vertical"> <Text ohos:height="match_content" ohos:width="match_content" ohos:text="媒体存储" ohos:text_size="28fp" ohos:text_color="#ff7f27" /> <Text ohos:height="2vp" ohos:width="match_parent" ohos:top_margin="10vp" ohos:bottom_margin="5vp" ohos:background_element="#ff7f27" /> <Button ohos:id="$+id:btnImages" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="图像文件" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnVideo" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="视频文件" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnAudio" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="音频文件" ohos:text_size="24fp" ohos:text_color="#ffffff" /> <Button ohos:id="$+id:btnDownloads" ohos:height="match_content" ohos:width="match_parent" ohos:padding="8vp" ohos:top_margin="10vp" ohos:background_element="$graphic:background_button" ohos:text="下载文件" ohos:text_size="24fp" ohos:text_color="#ffffff" /> </DirectionalLayout>
...\AVStorage\entry\src\main\java\com\minwei\avstorage\slice\MainAbilitySlice.java
public class MainAbilitySlice extends AbilitySlice { private static final HiLogLabel label = new HiLogLabel( HiLog.LOG_APP, 0x00101, MainAbilitySlice.class.getCanonicalName()); @Override public void onStart(Intent intent) { ... findComponentById(ResourceTable.Id_btnImages) .setClickedListener(component -> onImages()); findComponentById(ResourceTable.Id_btnVideo) .setClickedListener(component -> onVideo()); findComponentById(ResourceTable.Id_btnAudio) .setClickedListener(component -> onAudio()); findComponentById(ResourceTable.Id_btnDownloads) .setClickedListener(component -> onDownloads()); } ... private void onImages() { DataAbilityHelper helper = DataAbilityHelper.creator(this); ResultSet result = null; try { result = helper.query(Images.Media.EXTERNAL_DATA_ABILITY_URI, new String[]{Images.Media.ID}, null); HiLog.info(label, "图像文件数量:%{public}d", result.getRowCount()); HiLog.info(label, "图像文件列表:"); while (result.goToNextRow()) { int id = result.getInt(0); Uri uri = Uri.appendEncodedPathToUri( Images.Media.EXTERNAL_DATA_ABILITY_URI, String.valueOf(id)); HiLog.info(label, uri.toString()); /* FileDescriptor fd = helper.openFile(uri, "r"); FileReader fr = new FileReader(fd); // ... */ } } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } finally { if (result != null) result.close(); } } private void onVideo() { DataAbilityHelper helper = DataAbilityHelper.creator(this); ResultSet result = null; try { result = helper.query(Video.Media.EXTERNAL_DATA_ABILITY_URI, new String[]{Video.Media.ID}, null); HiLog.info(label, "视频文件数量:%{public}d", result.getRowCount()); HiLog.info(label, "视频文件列表:"); while (result.goToNextRow()) { int id = result.getInt(0); Uri uri = Uri.appendEncodedPathToUri( Video.Media.EXTERNAL_DATA_ABILITY_URI, String.valueOf(id)); HiLog.info(label, uri.toString()); /* FileDescriptor fd = helper.openFile(uri, "r"); FileReader fr = new FileReader(fd); // ... */ } } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } finally { if (result != null) result.close(); } } private void onAudio() { DataAbilityHelper helper = DataAbilityHelper.creator(this); ResultSet result = null; try { result = helper.query(Audio.Media.EXTERNAL_DATA_ABILITY_URI, new String[]{Audio.Media.ID}, null); HiLog.info(label, "音频文件数量:%{public}d", result.getRowCount()); HiLog.info(label, "音频文件列表:"); while (result.goToNextRow()) { int id = result.getInt(0); Uri uri = Uri.appendEncodedPathToUri( Audio.Media.EXTERNAL_DATA_ABILITY_URI, String.valueOf(id)); HiLog.info(label, uri.toString()); /* FileDescriptor fd = helper.openFile(uri, "r"); FileReader fr = new FileReader(fd); // ... */ } } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } finally { if (result != null) result.close(); } } private void onDownloads() { DataAbilityHelper helper = DataAbilityHelper.creator(this); ResultSet result = null; try { result = helper.query(Downloads.EXTERNAL_DATA_ABILITY_URI, new String[]{Downloads.ID}, null); HiLog.info(label, "下载文件数量:%{public}d", result.getRowCount()); HiLog.info(label, "下载文件列表:"); while (result.goToNextRow()) { int id = result.getInt(0); Uri uri = Uri.appendEncodedPathToUri( Downloads.EXTERNAL_DATA_ABILITY_URI, String.valueOf(id)); HiLog.info(label, uri.toString()); /* FileDescriptor fd = helper.openFile(uri, "r"); FileReader fr = new FileReader(fd); // ... */ } } catch (Exception exception) { HiLog.info(label, exception.getLocalizedMessage()); } finally { if (result != null) result.close(); } } }
运行效果如下图所示: