概述
在很多的React Native开发中,我们需要调用原生的api实现调用相机和图库的功能,网上用的最多的开源库如:react-native-image-picker。关于React-native-image-picker的用法大家请看相关的文档。我们今天手动实现一份。http://blog.csdn.net/xiangzhihong8/article/details/70245804
调用Android图库相机
创建项目
执行命令 :
react-native init HeadImage
创建一个名为HeadImage的工程,可以使用命令先运行下Demo项目。
然后照一张图片,放到放到工程的 HeadImage\Android\app\src\main\res\drawable 目录下。然后打开webstorm选择工程根目录,修改index.android.js代码如下:
export default class HeadImage extends Component {render() {return (container}>this._clickImage}>'head_default'}} style={{width:50,height:50}}/>);}_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);}@Overridepublic String getName() {return "HeadImageModule"; }@ReactMethodpublic void callCamera() { Log.d("","call camera...");}
}
HeadImagePackage.java
public class HeadImagePackage implements ReactPackage {&#64;Overridepublic List createNativeModules(ReactApplicationContext reactContext) {List modules &#61; new ArrayList<>();modules.add(new HeadImageModule(reactContext));return modules;}&#64;Overridepublic List<Class extends JavascriptModule>> createJSModules() {return Collections.emptyList();}&#64;Overridepublic List createViewManagers(ReactApplicationContext reactContext) {return Collections.emptyList();}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
在MainApplication注册模块
MainApplication.java
&#64;Override
protected List getPackages() {return Arrays.asList(new MainReactPackage(),new HeadImagePackage() );
}
js调用Java代码
在index.android.js的_clickImage方法调用Java方法。代码如下&#xff1a;
_clickImage(){NativeModules.HeadImageModule.callCamera()
}
注&#xff1a;别忘了导包&#xff1a;import { NativeModules } from ‘react-native’;
到这里已经实现了js与原生的交互&#xff0c;接下来我们需要实现调用相机的具体逻辑了。在HeadImageModule.java里我们先定义几个常量&#xff1a;
/ 保存图片的sd卡路径
private static final String HEAD_IMAGE_PATH &#61; Environment.getExternalStorageDirectory().getAbsolutePath() &#43; "/HeadImage/";
private static final String HEAD_IMAGE_NAME &#61; "head_image.png";
private static final int REQUEST_CODE_CAMERA &#61; 0;
private static final int REQUEST_CODE_GALLERY &#61; 1;
private static final int REQUEST_CODE_CROP &#61; 2;
接下来实现callCamera方法&#xff08;注&#xff1a;要让js可以调用必须加&#64;ReactMethod&#xff0c;Promise&#xff09;&#xff0c;callCamera相关代码如下&#xff1a;
&#64;ReactMethod
public void callCamera(Promise promise) {recursionDeleteFile(); Intent intent &#61; new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (isPathExists()) { mFullPath &#61; HEAD_IMAGE_PATH &#43; System.currentTimeMillis() &#43; ".png"; mUri &#61; Uri.fromFile(new File(mFullPath));intent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);Activity activity &#61; getCurrentActivity();if (activity !&#61; null) {mPromise &#61; promise;activity.startActivityForResult(intent, REQUEST_CODE_CAMERA);}}
}
执行完这个方法就可以启动相机了&#xff0c;但是这样每次调用相机都会创建一个临时图片&#xff0c;为了不使sd卡存头像图片的文件夹越来越大&#xff0c;所以编写了recursionDeleteFile()方法每次做一次递归删除&#xff0c;删除临时图片。拍照点击完成之后&#xff0c;就该去onActivityResult里面处理了&#xff0c;rn提供了一个接口实现监听onActivityResult&#xff0c;在HeadImageModule.java构造方法里面添加如下代码&#xff1a;
reactContext.addActivityEventListener(new BaseActivityEventListener() {&#64;Overridepublic void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {if (requestCode &#61;&#61; REQUEST_CODE_CAMERA) { if (resultCode &#61;&#61; Activity.RESULT_OK) { activity.startActivityForResult(cropImage(mUri), REQUEST_CODE_CROP);} else if (resultCode &#61;&#61; Activity.RESULT_CANCELED) { mPromise.resolve(null);new File(mFullPath).delete();}} else if (requestCode &#61;&#61; REQUEST_CODE_CROP) { if (resultCode &#61;&#61; Activity.RESULT_OK) {mPromise.resolve(mUri.toString());saveHeadImage();} else if (resultCode &#61;&#61; Activity.RESULT_CANCELED) {mPromise.resolve(null);new File(mFullPath).delete();}}}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
拍照完成之后就是本地裁剪图片了&#xff0c;这里不再讲解&#xff0c;后面大家直接看代码。裁剪完成之后&#xff0c;返回给js的图片是临时图片&#xff0c;而不是saveHeadImage()保存最终图片之后返回最终的图片。这里我们需要手动保存一份图片&#xff0c;代码如下&#xff1a;
if (resultCode &#61;&#61; Activity.RESULT_OK) {mPromise.resolve(mUri.toString())// 将临时图片复制一份&#xff0c;保存为最终的头像图片saveHeadImage()}
到这里&#xff0c;头像图片已经成功的保存到sd卡上了&#xff0c;接下来就是js显示的实现了&#xff0c;js需要处理的图片包括三个&#xff1a;默认头像&#xff0c;sd卡存的临时头像&#xff0c;sd卡存的最终头像&#xff0c;至于显示的时候我们先取最终头像&#xff0c;然后取临时头像。
新建MyImage.js
import React, {Component, PropTypes} from &#39;react&#39;;
import {View,StyleSheet,Image,NativeModules,
} from &#39;react-native&#39;;export default class MyImage extends Component {constructor(props) {super(props);this.state &#61; {uri: null,};}static defaultProps &#61; {uri: null,};static propTypes &#61; {uri: PropTypes.string,imageStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),}async componentWillReceiveProps() {let isExists &#61; await NativeModules.HeadImageModule.isImageExists();if (this.props.uri !&#61;&#61; null) {this.setState({uri: this.props.uri});} else if (isExists) {this.setState({uri: await NativeModules.HeadImageModule.getImageUri()});} else {this.setState({uri: &#39;head_default&#39;});}}render() {return (this.state.uri}} style&#61;{this.props.imageStyle}/>);}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
修改index.android.js代码&#xff1a;
export default class HeadImage extends Component {constructor(props) {super(props);this.state &#61; {headImageUri: null,};}render() {return (container}>this._clickImage.bind(this)}>this.state.headImageUri} imageStyle&#61;{{width: 100,height: 100}}/>);}async _clickImage() {this.setState({headImageUri: await NativeModules.HeadImageModule.callCamera() });}componentDidMount() {this.setState({code: this.props.code});}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
附&#xff1a;源码