前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React Native调用Android相机图库

React Native调用Android相机图库

作者头像
xiangzhihong
发布2018-02-06 16:48:23
2K0
发布2018-02-06 16:48:23
举报
文章被收录于专栏:向治洪向治洪

概述

在很多的React Native开发中,我们需要调用原生的api实现调用相机和图库的功能,网上用的最多的开源库如:react-native-image-picker。关于react-native-image-picker的用法大家请看相关的文档。我们今天手动实现一份。

调用Android图库相机

创建项目

执行命令 :

代码语言:javascript
复制
react-native init HeadImage 

创建一个名为HeadImage的工程,可以使用命令先运行下Demo项目。 然后照一张图片,放到放到工程的 HeadImage\Android\app\src\main\res\drawable 目录下。然后打开webstorm选择工程根目录,修改index.android.js代码如下:

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
@Override
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new HeadImagePackage()  //注册模块
    );
}

js调用Java代码

在index.android.js的_clickImage方法调用Java方法。代码如下:

代码语言:javascript
复制
_clickImage(){
    NativeModules.HeadImageModule.callCamera()
}

注:别忘了导包:import { NativeModules } from ‘react-native’; 到这里已经实现了js与原生的交互,接下来我们需要实现调用相机的具体逻辑了。在HeadImageModule.java里我们先定义几个常量:

代码语言:javascript
复制
/ 保存图片的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相关代码如下:

代码语言:javascript
复制
@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构造方法里面添加如下代码:

代码语言:javascript
复制
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()保存最终图片之后返回最终的图片。这里我们需要手动保存一份图片,代码如下:

代码语言:javascript
复制
  if (resultCode == Activity.RESULT_OK) {
                mPromise.resolve(mUri.toString());
                // 将临时图片复制一份,保存为最终的头像图片
                saveHeadImage();
            } 

到这里,头像图片已经成功的保存到sd卡上了,接下来就是js显示的实现了,js需要处理的图片包括三个:默认头像,sd卡存的临时头像,sd卡存的最终头像,至于显示的时候我们先取最终头像,然后取临时头像。 新建MyImage.js

代码语言:javascript
复制
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代码:

代码语言:javascript
复制
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
        });
    }
}

附:源码

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-04-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 调用Android图库相机
    • 创建项目
      • 添加React Native和原生的交互
        • js调用Java代码
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档