600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > android native 相册 React Native手动实现调用原生相机相册(Android端)

android native 相册 React Native手动实现调用原生相机相册(Android端)

时间:2021-12-21 09:16:39

相关推荐

android native 相册 React Native手动实现调用原生相机相册(Android端)

前言

最近一直在学习RN的相关开发,想做一部分调用原生的实现,来练练手,于是就有了下面这个demo,由于本人是Android开发,所以只实现了Android端的效果.

Demo

主要实现

这种调用原生的操作,主要涉及到RN于原生交互和通信,官方给出的交互方式就是通过在Application中注册的模式实现交互.

首先我们需要先创建一个RN项目 CameraPage.js ,并把它设为启动页:页面比较简单,就是两个按钮拍照,照片一个Image标签用来展示原生的图片.开始新建与原生交互的类, CameraMoudle 继承自ReactContextBaseJavaModule类:

public class CameraMoudle extends ReactContextBaseJavaModule {

private ReactApplicationContext reactContext;

public CameraMoudle(ReactApplicationContext reactContext) {

super(reactContext);

this.reactContext =reactContext;

}

@Override

public String getName() {

return "CameraMoudle";

}

//打开登录设置界面,用于给RN调用

@ReactMethod

public void openNative(int tag) {

Intent intent = new Intent();

CameraAct.reactContext=reactContext;

CameraAct.tag=tag;

intent.setClass(reactContext, CameraAct.class);

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

reactContext.startActivity(intent);

}

}

这个getName()方法中返回的就是RN端调用的引用名,相当于webview中addJavascriptInterface(JsInterface,JsName)中的JsName,下面的openNative方法是我们自定义的Rn端调用原生的方法.注意:这里面的reactContext是静态赋值.接下来创建CameraPackage.java继承ReactPackage这个CameraMoudle需要通过这个类在Application中注册才能使用:

public class CameraPackage implements ReactPackage {

@Override

public List createNativeModules(ReactApplicationContext reactContext) {

List modules = new ArrayList<>();

modules.add(new CameraMoudle(reactContext));

return modules;

}

@Override

public List createViewManagers(ReactApplicationContext reactContext) {

return Collections.emptyList();

}

}

在Application中新增标识的代码

@Override

protected List getPackages() {

return Arrays.asList(

new MainReactPackage(),

new CameraPackage()//新增包操作

);

}

新建一个空ActivityCameraAct.java,用来执行,跳转相机和相册的逻辑操作,由于不需要界面所有就不用setContentView啦!

拍照和相册相关操作:

private void choosePhoto() {

Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null);

intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/jpeg");

startActivityForResult(intentToPickPic, PHOTO_REQUEST_CODE);

}

private void takePhoto() {

// 跳转到系统的拍照界面

Intent intentToTakePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

// 指定照片存储位置为sd卡本目录下

mPhotoPath = Environment.getExternalStorageDirectory() + File.separator + "photo.jpeg";

// 获取图片所在位置的Uri路径 由于使用的tagBuildVersion 26需要通过内容提供者获取文件写入操作

mImageUri = FileProvider.getUriForFile(this,

getApplicationContext().getPackageName() + ".my.provider",

new File(mPhotoPath));

//下面这句指定调用相机拍照后的照片存储的路径

intentToTakePhoto.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);

startActivityForResult(intentToTakePhoto, CAMERA_REQUEST_CODE);

}

//当拍摄照片完成时会回调到onActivityResult 在这里处理照片的裁剪

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {

if (resultCode == RESULT_OK) {

switch (requestCode) {

case CAMERA_REQUEST_CODE: {

sendToRn();

break;

}

case PHOTO_REQUEST_CODE: {

Uri mImageUri=data.getData();

if (!TextUtils.isEmpty(mImageUri.getAuthority())) {

String[] proj = {MediaStore.Images.Media.DATA};

Cursor cursor = managedQuery(mImageUri, proj, null, null,

null);

int column_index = cursor

.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

cursor.moveToFirst();

mPhotoPath = cursor.getString(column_index);

}else{

mPhotoPath= mImageUri.getPath();

}

sendToRn();

break;

}

default:

break;

}

}

super.onActivityResult(requestCode, resultCode, data);

}

由于我本地targetSdkVersion为26 所以需要动态申请权限和通过内容提供者获取文件读取操作(如果使用 22 版本就都不需要啦!)

private boolean checkPermission(){

String [] permissions =new String[]{

Manifest.permission.WRITE_EXTERNAL_STORAGE,

Manifest.permission.CAMERA

};

for (String permission:permissions) {

if (ContextCompat.checkSelfPermission(CameraAct.this, permission) != PackageManager.PERMISSION_GRANTED){

ActivityCompat.requestPermissions(CameraAct.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA},

tag==0?CAMERA_REQUEST_CODE:PHOTO_REQUEST_CODE);

return false;

}

}

return true;

}

@Override

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

super.onRequestPermissionsResult(requestCode, permissions, grantResults);

if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

switch (requestCode) {

case CAMERA_REQUEST_CODE:

takePhoto();

break;

case PHOTO_REQUEST_CODE:

choosePhoto();

break;

default:

break;

}

} else {

Toast.makeText(this, "一些权限被禁止", Toast.LENGTH_SHORT).show();

}

}

入口调用方法

private void startCamera() {

if(checkPermission()){

if (tag==0) {

takePhoto();

}else {

choosePhoto();

}

}

}

为了防止静态赋值的reactContext内存泄露:

@Override

protected void onDestroy() {

super.onDestroy();

reactContext=null;

}

差点忘了权限添加:

内容提供者:临时权限配置

android:name="android.support.v4.content.FileProvider"

android:authorities="${applicationId}.my.provider"

android:exported="false"

android:grantUriPermissions="true">

android:name="android.support.FILE_PROVIDER_PATHS"

android:resource="@xml/provider_paths"/>

在android/src/main/res下新建xml文件夹 创建provider_paths.xml

至此android端的配置已经完成,接下来修改RN端代码:CameraPage.js增加以下代码:

getPhoto=(statusCode)=>{

//调用原生

CameraMoudle.openNative(statusCode);

//注册监听

DeviceEventEmitter.addListener("photo", (params) => {

let index =params.indexOf('storage');

let file='file:///'+params.substr(index==-1?1:index);

this.setState({

imgFile:file

})

});

}

statusCode是用来区分照相和相册选择的,通用处理了一下,Image标签:

{this.state.imgFile===''?null:}

以上调用就可以实现Rn调用原生的操作.

另外

RN与原生交互的方式有3中:RCTDeviceEventEmitter,Callback,Promise;其中第一种方式是唯一一种原生主动发送消息给RN的.

另外两种都是RN端作为主动方,原生被动返回.

以上如有疏漏的地方,望不吝赐教.

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。