React Native调用Android相机图库
概述
在很多的React Native开发中,我们需要调用原生的api实现调用相机和图库的功能,网上用的最多的开源库如:react-native-image-picker。关于react-native-image-picker的用法大家请看相关的文档。我们今天手动实现一份。
调用Android图库相机
创建项目
执行命令 :
react-native init HeadImage
创建一个名为HeadImage的工程,可以使用命令先运行下Demo项目。 然后照一张图片,放到放到工程的 HeadImageAndroidappsrcmainresdrawable 目录下。然后打开webstorm选择工程根目录,修改index.android.js代码如下:
export default class HeadImage extends Component {
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._clickImage}>
<Image source={{uri: 'head_default'}} style={{width:50,height:50}}/>
</TouchableOpacity>
</View>
);
}
_clickImage(){
console.log("click image...");
}
}
添加React Native和原生的交互
新建两个类,HeadImageModule.Java和HeadImagePackage.java,分别继承ReactContextBaseJavaModule和ReactPackage,之后在MainApplication.java里面注册。代码如下: HeadImageModule.java
public class HeadImageModule extends ReactContextBaseJavaModule {
public HeadImageModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "HeadImageModule"; //注意这里的返回值
}
@ReactMethod
public void callCamera() { // 调用相机的方法
Log.d("","call camera...");
}
}
HeadImagePackage.java
public class HeadImagePackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new HeadImageModule(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
在MainApplication注册模块 MainApplication.java
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new HeadImagePackage() //注册模块
);
}
js调用Java代码
在index.android.js的_clickImage方法调用Java方法。代码如下:
_clickImage(){
NativeModules.HeadImageModule.callCamera()
}
注:别忘了导包:import { NativeModules } from ‘react-native’; 到这里已经实现了js与原生的交互,接下来我们需要实现调用相机的具体逻辑了。在HeadImageModule.java里我们先定义几个常量:
/ 保存图片的sd卡路径
private static final String HEAD_IMAGE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/HeadImage/";
// 保存图片的名称
private static final String HEAD_IMAGE_NAME = "head_image.png";
// startActivityForResult 的 requestCode
private static final int REQUEST_CODE_CAMERA = 0;
private static final int REQUEST_CODE_GALLERY = 1;
private static final int REQUEST_CODE_CROP = 2;
接下来实现callCamera方法(注:要让js可以调用必须加@ReactMethod,Promise),callCamera相关代码如下:
@ReactMethod
public void callCamera(Promise promise) {
recursionDeleteFile(); // 删除目录下除了头像图片的其他临时图片
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//启动相机的intent
if (isPathExists()) { // 判断常量定义的路径是否存在,不存在就创建,然后返回true
mFullPath = HEAD_IMAGE_PATH + System.currentTimeMillis() + ".png"; // 临时图片
mUri = Uri.fromFile(new File(mFullPath));
intent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);
Activity activity = getCurrentActivity();
if (activity != null) {
mPromise = promise;
activity.startActivityForResult(intent, REQUEST_CODE_CAMERA);
}
}
}
执行完这个方法就可以启动相机了,但是这样每次调用相机都会创建一个临时图片,为了不使sd卡存头像图片的文件夹越来越大,所以编写了recursionDeleteFile()方法每次做一次递归删除,删除临时图片。拍照点击完成之后,就该去onActivityResult里面处理了,rn提供了一个接口实现监听onActivityResult,在HeadImageModule.java构造方法里面添加如下代码:
reactContext.addActivityEventListener(new BaseActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_CAMERA) { // 调用相机回调
if (resultCode == Activity.RESULT_OK) { // *************1.拍照完成,将进入裁剪界面
activity.startActivityForResult(cropImage(mUri), REQUEST_CODE_CROP);// 启动裁剪界面
} else if (resultCode == Activity.RESULT_CANCELED) { // 拍照界面点击取消
mPromise.resolve(null);
// mFullPath就是callCamera里面定义的临时图片路径
// 如果没有取消拍照,那么就不执行这里,临时图片的删除将在下次调用相机的时候,所以与recursionDeleteFile()不重复
new File(mFullPath).delete();
}
} else if (requestCode == REQUEST_CODE_CROP) { // ************2.裁剪完成
if (resultCode == Activity.RESULT_OK) {
// uri存的是临时图片路径,返回给js代码,这里有个问题,稍后再说
mPromise.resolve(mUri.toString());
// 将临时图片复制一份,保存为最终的头像图片
saveHeadImage();
} else if (resultCode == Activity.RESULT_CANCELED) {
mPromise.resolve(null);
new File(mFullPath).delete();
}
}
}
});
拍照完成之后就是本地裁剪图片了,这里不再讲解,后面大家直接看代码。裁剪完成之后,返回给js的图片是临时图片,而不是saveHeadImage()保存最终图片之后返回最终的图片。这里我们需要手动保存一份图片,代码如下:
if (resultCode == Activity.RESULT_OK) {
mPromise.resolve(mUri.toString());
// 将临时图片复制一份,保存为最终的头像图片
saveHeadImage();
}
到这里,头像图片已经成功的保存到sd卡上了,接下来就是js显示的实现了,js需要处理的图片包括三个:默认头像,sd卡存的临时头像,sd卡存的最终头像,至于显示的时候我们先取最终头像,然后取临时头像。 新建MyImage.js
import React, {Component, PropTypes} from 'react';
import {
View,
StyleSheet,
Image,
NativeModules,
} from 'react-native';
export default class MyImage extends Component {
constructor(props) {
super(props);
this.state = {
uri: null,
};
}
static defaultProps = {
uri: null,
};
static propTypes = {
uri: PropTypes.string,
imageStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
}
async componentWillReceiveProps() {
let isExists = await NativeModules.HeadImageModule.isImageExists();
if (this.props.uri !== null) {
this.setState({
uri: this.props.uri
});
} else if (isExists) {
this.setState({
uri: await NativeModules.HeadImageModule.getImageUri()
});
} else {
this.setState({
uri: 'head_default'
});
}
}
render() {
return (
<Image source={{uri: this.state.uri}} style={this.props.imageStyle}/>
);
}
}
修改index.android.js代码:
export default class HeadImage extends Component {
constructor(props) {
super(props);
this.state = {
headImageUri: null,
};
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._clickImage.bind(this)}>
<MyImage uri={this.state.headImageUri} imageStyle={{width: 100,height: 100}}/>
</TouchableOpacity>
</View>
);
}
async _clickImage() {
this.setState({
headImageUri: await NativeModules.HeadImageModule.callCamera() // 相机拍照
// headImageUri: await NativeModules.HeadImageModule.callGallery() // 相册选择图片
});
}
componentDidMount() {
this.setState({
code: this.props.code
});
}
}
附:源码
- .NET4.0下web应用程序用UrlRewriter.dll重写无后缀路径
- Silverlight中摄像头的运用—part2
- 区块链小白投资入门操作指南(上)
- 《我的WCF之旅》博文系列汇总
- 网站出现“Service Unavailable”提示该如何解决
- Silverlight 4 中摄像头的运用—part1
- Silverlight 4 中摄像头的运用—part1
- Silverlight 4 中数据绑定发生的变化
- 未来3年 人工智能如何影响法律行业?5位权威专家给出趋势
- silverlight.js详解.
- 前端三大框架vue,angular,react大杂烩
- Silverlight制作scrollbar.
- [Silverlight动画]转向行为 - 躲避行为
- WCF后续之旅(1): WCF是如何通过Binding进行通信的
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- CentOS 7如何实现定时执行python脚本
- PHP自动生成缩略图函数的源码示例
- 解决tensorflow 释放图,删除变量问题
- php生成word并下载代码实例
- TensorFlow保存TensorBoard图像操作
- 浅谈PHP SHA1withRSA加密生成签名及验签
- PHP PDO数据库操作预处理与注意事项
- laravel 框架配置404等异常页面
- Django –Xadmin 判断登录者身份实例
- Laravel 队列使用的实现
- keras 两种训练模型方式详解fit和fit_generator(节省内存)
- Keras 中Leaky ReLU等高级激活函数的用法
- Python错误的处理方法
- python3的pip路径在哪
- MySQL 案例:init_connect 引发的 Lost connection