react-native调用原生安卓android(兼容7.0以上版本)系统相机拍照和选择相册照片,并将返回结果用作头像

Rhea ·
更新时间:2024-09-20
· 703 次阅读

话不多说,先上效果图

在这里插入图片描述

大致的思路

要从RN中调用原生相机拍照和选择照片思路是这样的,先在原生中创建一个ReactContextBaseJavaModule模块,并在该模块中显示调用原生相机和选择照片的方法。然后,将该模块添加到ReactPackage列表中。最后,在js中导入该模块使用。关于原生模块的创建以及在js中如何调用请参考下面这篇文章:
https://blog.csdn.net/weixin_38824257/article/details/103905668

下面是调用原生拍照和选择相册的具体实现 1.编写提供给RN调用的拍照和选择照片的方法。

首先创建一个继承自ReactContextBaseJavaModule的类。在该类中编写暴露给RN调用的拍照方法,注意这里有一个传入的参数Promise(这个参数不需要从RN中传入),是用于返回数据给RN的。注解@ReactMethod表示该方法是该模块下可被RN调用的方法。

/** * 拍照方法,提供给js调用的方法 * @param promise 返回拍照结果给js需要使用 */ @ReactMethod public void openCamera(Promise promise) { //安卓6.0以上需要动态申请权限 if (hasPermission()) { mPromise = promise; takePhoto(); } }

检查是否已经获得拍照和读写内存卡的权限,android6.0以上需要用户同意获取。

private boolean hasPermission() { currentActivity = getCurrentActivity(); //安卓6.0以上动态权限申请 if (Build.VERSION.SDK_INT >= 23) { int checkCallPhonePermission = ContextCompat.checkSelfPermission(reactContext, Manifest.permission.CAMERA); if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(currentActivity, PERMISSIONS_STORAGE, OPEN_CAMERA); return false; } else { return true; } } else { return true; } }

以下是拍照和读写内存卡的权限

private static String[] PERMISSIONS_STORAGE = { "android.permission.CAMERA", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"};//权限

下面是调起原生相机的taokePhoto方法,里面为了兼容android7.0以上的系统,需要使用FileProvider共享文件的方式来获取文件uri。使用FileProvider需要再做一些额外的配置。

private void takePhoto() { mIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File faceDir = getImageDir(); if (faceDir != null) { mFileName = System.currentTimeMillis() + ".jpg"; File file = new File(faceDir, mFileName); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //设置7.0以上共享文件,分享路径定义在xml/file_paths.xml mUri = FileProvider.getUriForFile(reactContext, "com.github_rn.fileprovider", file); } else { // 7.0以下,共享文件 mUri = Uri.fromFile(file); } mIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); mIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); mIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); mIntent.putExtra(MediaStore.EXTRA_OUTPUT, mUri); currentActivity.startActivityForResult(mIntent, CAMERA_CODE); } }

配置FileProvider,在AndroidManifest.xml文件中的application节点下加入如下配置,注意这里的android:authorities字段需要跟拍照时使用的FileProvider.getUriForFile方法传入的第二个参数一致:

然后在res目录下创建xml文件夹,在文件夹下创建file_paths.xml文件。文件的内容如下,path表示需要共享的目录,name名字随便取就可以。

<!--

还需要在AndroidManifest.xml添加如下权限:

下面是选择相册照片的方法:

/** * 打开相册选择照片 * @param promise */ @ReactMethod public void chooseAlbum(Promise promise) { if (hasPermission()) {//申请权限 mPromise = promise; Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); currentActivity.startActivityForResult(intent, ALBUM_CODE); } } 监听拍照和选择照片返回的结果

在该模块创建时,添加addActivityEventListener方法,方法如下。在initActivityEventListener方法的onActivityResult方法中得到拍照和选择照片返回的结果,并且使用promise将结果返回给RN。

public MyIntentModule(@NonNull ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; initActivityEventListener(); } @NonNull @Override public String getName() { return "AlbumModule"; } private void initActivityEventListener() { reactContext.addActivityEventListener(new BaseActivityEventListener() { @Override public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { if (requestCode == CAMERA_CODE) { if (resultCode == Activity.RESULT_OK) { mPromise.resolve(IMAGE_ROOT_PATH + mFileName); } else if (resultCode == Activity.RESULT_CANCELED) { mPromise.resolve(null); } } else if (requestCode == ALBUM_CODE) { if (resultCode == Activity.RESULT_OK) { try { Uri selectedImage = data.getData(); //获取系统返回的照片的Uri String[] filePathColumn = {MediaStore.Images.Media.DATA}; Cursor cursor = reactContext.getContentResolver().query(selectedImage, filePathColumn, null, null, null);//从系统表中查询指定Uri对应的照片 cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String path = cursor.getString(columnIndex); //获取照片路径 cursor.close(); Log.i("callCamera", "path:" + path); mPromise.resolve(path); } catch (Exception e) { // TODO Auto-generatedcatch block e.printStackTrace(); mPromise.resolve(null); } } else if (resultCode == Activity.RESULT_CANCELED) { mPromise.resolve(null); } } } }); } 在RN中调用并接收返回结果

先导入NativeModules

import {View, Text, StyleSheet, Image, TouchableOpacity, ScrollView, NativeModules, AsyncStorage} from "react-native";

在RN的点击事件中调用拍照或者选择照片方法

//拍照 openCamera() { NativeModules.AlbumModule.openCamera().then((res) => { console.log("openCamera--res:" + res); if (res) { this.setState({ imagePath: res, }); this.setHeadImage(res); } }); } //选择照片 chooseAlbum(){ NativeModules.AlbumModule.chooseAlbum().then((res)=>{ console.log("chooseAlbum--res:" + res); if (res) { this.setState({ imagePath: res, }); this.setHeadImage(res); } }) } //将照片路径保存到Storage中 setHeadImage(imagePath) { AsyncStorage.setItem(HEAD_IMAGE, imagePath); }

最后,在RN中显示该照片。RN中Image组件可以显示手机本地照片,source的路径为:“file:///”+“照片的路径”

} 具体源码地址

运行项目前,需要先用android studio下载好依赖
https://github.com/githubchl/Github_RN


作者:副排长



兼容 选择 版本 系统 native 相机 React Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号