react-native調用原生安卓android(兼容7.0以上版本)系統相機拍照和選擇相冊照片,並將返回結果用作頭像

話不多說,先上效果圖

在這裏插入圖片描述

大致的思路

要從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方法傳入的第二個參數一致:

  <provider
           android:name="androidx.core.content.FileProvider"
            android:authorities="com.github_rn.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

然後在res目錄下創建xml文件夾,在文件夾下創建file_paths.xml文件。文件的內容如下,path表示需要共享的目錄,name名字隨便取就可以。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <external-path
        name="camera_photos"
        path="" /><!--不寫代表共享整個sd卡-->

    <!--<external-path-->
        <!--name="camera_photos"-->
        <!--path="faces/" />共享sd卡下的faces目錄-->
</resources>

還需要在AndroidManifest.xml添加如下權限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

下面是選擇相冊照片的方法:

 /**
     * 打開相冊選擇照片
     * @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:///”+“照片的路徑”

<Image source={{uri: `file:///${this.state.imagePath}`}}
       style={{width: 60, height: 60,borderRadius:5}}/>}

具體源碼地址

運行項目前,需要先用android studio下載好依賴
https://github.com/githubchl/Github_RN

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章