app混合开发,选择相册和相机功能

原生:安卓 , IOS

前端:vue

目标:前端 input type=file 实现调用原生相机和图片,选择并返回前台照片

前提:安卓调用和访问权限一添加

原生: 

dWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
                mFileCallback = filePathCallback;
                MODE_OPEN_MULTIPLE = fileChooserParams.getMode();
                showImageSelectDialog();
                return true;
            }
});
onShowFileChooser 获取 input的信息


安卓端弹窗开发

 

//自主选择相机或者相册dialog
    private boolean isClick = false;

    private void showImageSelectDialog() {
        isClick = false;
        final Dialog dialog = new Dialog(this, R.style.Dialog);
        View view = View.inflate(this, R.layout.dialog_custom_layout, null);
        dialog.setContentView(view);

        Window window = dialog.getWindow();
        window.setGravity(Gravity.BOTTOM);
        window.setWindowAnimations(R.style.main_menu_animStyle);
        window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        dialog.show();

        //打开相机按钮
        dialog.findViewById(R.id.tv_take_photo).setOnClickListener(view1 -> {
            isClick = true;
            openCamera();
            dialog.dismiss();
        });
        //打开相册按钮
        dialog.findViewById(R.id.tv_take_pic).setOnClickListener(view2 -> {
            isClick = true;
            takePhoto();
            dialog.dismiss();
        });

        //取消按钮
        dialog.findViewById(R.id.tv_cancel).setOnClickListener(view3 -> dialog.dismiss());
        //关闭dialog
        dialog.setOnDismissListener(dialog1 -> {
            if ( ! isClick ) {
                mFileCallback.onReceiveValue(null);
            }
        });
    }

开发打开相机

//打开相机
    private void openCamera() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File p = Environment.getExternalStorageDirectory();
        File dir = new File(FileUtil.getImageTempPath());
        if ( ! dir.exists() ) {
            dir.mkdirs();
        }
        String picName = System.currentTimeMillis() + ".jpg";
        String appId = ConfigUtils.getFieldBuildConfig("APPLICATION_ID");
        File photoFile = new File(FileUtil.getImageTempPath() + picName);
        if ( Build.VERSION.SDK_INT >= 24 ) {
            Uri uri = FileProvider.getUriForFile(context, appId + ".fileprovider", photoFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        } else {
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
        }
        CacheUtils.getInstance().setCameraPhotoPath(this, photoFile.getAbsolutePath());
        startActivityForResult(intent, REQUEST_CODE_CAMERA);
    }

打开相册

//打开相册
    private void takePhoto() {
        //传递读取本地相册意图
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType("image/*");
        if(MODE_OPEN_MULTIPLE == 1){
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        }
        // 判断系统中是否有处理该 Intent 的 Activity
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CODE_PICTURE);
        } else {
            Toast.makeText(MainActivity.this, "未找到图片查看器", Toast.LENGTH_SHORT).show();
        }
    }

处理回调

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {


        //input控件相关回调设置
        if ( resultCode == RESULT_OK ) {
            switch (requestCode) {
                case REQUEST_CODE_CAMERA:
                    String path = CacheUtils.getInstance().getCameraPhotoPath(this);
                    File file = new File(path);
                    if ( file.exists() ) {
                        mFileCallback.onReceiveValue(new Uri[]{Uri.fromFile(file)});
                    } else {
                        mFileCallback.onReceiveValue(null);
                    }
                    mFileCallback = null;
                    break;
                case REQUEST_CODE_PICTURE:
                    if(data != null){
                        Uri uri = data.getData();
                        List<Uri> uriList = new ArrayList<>();
                        if (uri != null) {
                            //没设置Intent.EXTRA_ALLOW_MULTIPLE,会回调这个
                            uriList.add(uri);
                        } else if (data.getClipData() != null) {
                            //设置Intent.EXTRA_ALLOW_MULTIPLE,会回调这个
                            ClipData clipData = data.getClipData();
                            int count = clipData.getItemCount();
                            if(count > 0){
                                for (int i=0; i<count; i++){
                                    Uri imageUri =clipData.getItemAt(i).getUri();
                                    uriList.add(imageUri);
                                }
                            }else {
                                Toast.makeText(context , "获取数据为空", Toast.LENGTH_LONG).show();
                            }
                        } else {
                            Toast.makeText(context , "获取数据为空", Toast.LENGTH_LONG).show();
                        }
                        if(CollectionUtil.isNotEmpty(uriList)){
                            Uri[] uris = uriList.toArray(new Uri[uriList.size()]);
                            mFileCallback.onReceiveValue(uris);
                        }
                    } else {
                        Toast.makeText(context , "获取数据为空", Toast.LENGTH_LONG).show();
                    }
                    mFileCallback = null;
                    break;
                default:
                    break;
            }
        }
        if ( mFileCallback != null ) {
            mFileCallback.onReceiveValue(null);
            mFileCallback = null;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

 

弹窗画面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_take_photo"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="拍摄"
        android:textColor="@android:color/background_dark"
        android:textSize="16sp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:background="#1a000000" />

    <TextView
        android:id="@+id/tv_take_pic"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="从手机相册选择"
        android:textColor="@android:color/background_dark"
        android:textSize="16sp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="6dp"
        android:background="#08000000" />

    <TextView
        android:id="@+id/tv_cancel"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="取消"
        android:textColor="@android:color/background_dark"
        android:textSize="16sp" />

</LinearLayout>

 

动画主题

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Dialog" parent="android:Theme.Dialog">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
    </style>

    <style name="main_menu_animStyle">
        <item name="android:windowEnterAnimation">@anim/dialog_in_anim</item>
        <item name="android:windowExitAnimation">@anim/dialog_out_anim</item>
    </style>
</resources>

动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="500"
        android:fromXDelta="0"
        android:fromYDelta="1000"
        android:toXDelta="0"
        android:toYDelta="0" />
</set>

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="500"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="1000" />
</set>

 

 

完整代码

 

package com.baosight.wh.app;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import cn.hutool.core.collection.CollectionUtil;
import com.baosight.wh.app.bridge.appInfo.BSBridge;
import com.baosight.wh.app.utils.CacheUtils;
import com.baosight.wh.app.utils.ConfigUtils;
import com.baosight.wh.app.utils.FileUtil;
import wendu.dsbridge.DWebView;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
//import com.baosight.wh.app.bridge.config.BSWebView;
//import com.baosight.wh.app.bridge.config.BSWebViewClient;
//import com.baosight.wh.app.bridge.config.JSResolver;


public class MainActivity extends AppCompatActivity {

    private static final String MAIN_URL = "MAIN_URL";

    private DWebView dWebView;

    //input文件回调
    private ValueCallback<Uri[]> mFileCallback;

    @SuppressLint("StaticFieldLeak")
    public static Context context;

    //上传文件常量
    public static final int REQUEST_CODE_CAMERA = 0x4001;
    public static final int REQUEST_CODE_PICTURE = 0x4002;

    private static int MODE_OPEN_MULTIPLE = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bridge_webview);
        context = this.getApplicationContext();
        dWebView = findViewById(R.id.mainBridgeView);
        dWebView.addJavascriptObject(new BSBridge(this) , null);
            String mainUrl = ConfigUtils.getFieldBuildConfig(MAIN_URL);
        dWebView.loadUrl(mainUrl);
        //定义拍照
        dWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
                mFileCallback = filePathCallback;
                MODE_OPEN_MULTIPLE = fileChooserParams.getMode();
                showImageSelectDialog();
                return true;
            }


        });
    }

    //自主选择相机或者相册dialog
    private boolean isClick = false;

    private void showImageSelectDialog() {
        isClick = false;
        final Dialog dialog = new Dialog(this, R.style.Dialog);
        View view = View.inflate(this, R.layout.dialog_custom_layout, null);
        dialog.setContentView(view);

        Window window = dialog.getWindow();
        window.setGravity(Gravity.BOTTOM);
        window.setWindowAnimations(R.style.main_menu_animStyle);
        window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        dialog.show();

        //打开相机按钮
        dialog.findViewById(R.id.tv_take_photo).setOnClickListener(view1 -> {
            isClick = true;
            openCamera();
            dialog.dismiss();
        });
        //打开相册按钮
        dialog.findViewById(R.id.tv_take_pic).setOnClickListener(view2 -> {
            isClick = true;
            takePhoto();
            dialog.dismiss();
        });

        //取消按钮
        dialog.findViewById(R.id.tv_cancel).setOnClickListener(view3 -> dialog.dismiss());
        //关闭dialog
        dialog.setOnDismissListener(dialog1 -> {
            if ( ! isClick ) {
                mFileCallback.onReceiveValue(null);
            }
        });
    }

    //打开相机
    private void openCamera() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File p = Environment.getExternalStorageDirectory();
        File dir = new File(FileUtil.getImageTempPath());
        if ( ! dir.exists() ) {
            dir.mkdirs();
        }
        String picName = System.currentTimeMillis() + ".jpg";
        String appId = ConfigUtils.getFieldBuildConfig("APPLICATION_ID");
        File photoFile = new File(FileUtil.getImageTempPath() + picName);
        if ( Build.VERSION.SDK_INT >= 24 ) {
            Uri uri = FileProvider.getUriForFile(context, appId + ".fileprovider", photoFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        } else {
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
        }
        CacheUtils.getInstance().setCameraPhotoPath(this, photoFile.getAbsolutePath());
        startActivityForResult(intent, REQUEST_CODE_CAMERA);
    }

    //打开相册
    private void takePhoto() {
        //传递读取本地相册意图
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType("image/*");
        if(MODE_OPEN_MULTIPLE == 1){
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        }
        // 判断系统中是否有处理该 Intent 的 Activity
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CODE_PICTURE);
        } else {
            Toast.makeText(MainActivity.this, "未找到图片查看器", Toast.LENGTH_SHORT).show();
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {


        //input控件相关回调设置
        if ( resultCode == RESULT_OK ) {
            switch (requestCode) {
                case REQUEST_CODE_CAMERA:
                    String path = CacheUtils.getInstance().getCameraPhotoPath(this);
                    File file = new File(path);
                    if ( file.exists() ) {
                        mFileCallback.onReceiveValue(new Uri[]{Uri.fromFile(file)});
                    } else {
                        mFileCallback.onReceiveValue(null);
                    }
                    mFileCallback = null;
                    break;
                case REQUEST_CODE_PICTURE:
                    if(data != null){
                        Uri uri = data.getData();
                        List<Uri> uriList = new ArrayList<>();
                        if (uri != null) {
                            //没设置Intent.EXTRA_ALLOW_MULTIPLE,会回调这个
                            uriList.add(uri);
                        } else if (data.getClipData() != null) {
                            //设置Intent.EXTRA_ALLOW_MULTIPLE,会回调这个
                            ClipData clipData = data.getClipData();
                            int count = clipData.getItemCount();
                            if(count > 0){
                                for (int i=0; i<count; i++){
                                    Uri imageUri =clipData.getItemAt(i).getUri();
                                    uriList.add(imageUri);
                                }
                            }else {
                                Toast.makeText(context , "获取数据为空", Toast.LENGTH_LONG).show();
                            }
                        } else {
                            Toast.makeText(context , "获取数据为空", Toast.LENGTH_LONG).show();
                        }
                        if(CollectionUtil.isNotEmpty(uriList)){
                            Uri[] uris = uriList.toArray(new Uri[uriList.size()]);
                            mFileCallback.onReceiveValue(uris);
                        }
                    } else {
                        Toast.makeText(context , "获取数据为空", Toast.LENGTH_LONG).show();
                    }
                    mFileCallback = null;
                    break;
                default:
                    break;
            }
        }
        if ( mFileCallback != null ) {
            mFileCallback.onReceiveValue(null);
            mFileCallback = null;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
}

 

前端代码

 <div class="blue_btn" @click="upload">
      <input type="file" ref="file" accept="image/*" @change="getPic"
        multiple="multiple" style='display:none' />上传文件
 </div>
async getPic(e) {
      var that = this
      const files = e.target.files;
      console.log(files);
      for (let i = 0; i < files.length; i++) {
        that.getBase64(files[i]).then(res => {
          that.base64List.push(res);
        })
      }
    },
    upload() {
      this.$refs.file.click();
    },
    /**
      * file 转Base64 DataURL
      * @param {File} file
      * @returns
      */
    getBase64(file) {
      return new Promise((resolve, reject) => {
        ///FileReader类就是专门用来读文件的
        const reader = new FileReader()
        //开始读文件
        //readAsDataURL: dataurl它的本质就是图片的二进制数据, 进行base64加密后形成的一个字符串,
        reader.readAsDataURL(file)
        // 成功和失败返回对应的信息,reader.result一个base64,可以直接使用
        reader.onload = () => resolve(reader.result)
        // 失败返回失败的信息
        reader.onerror = error => reject(error)
      })
    },

注:如果调用相机闪退,取相册数据为空,请检查相机相册已经媒体访问的权限是否加入

posted @ 2023-03-24 15:48  未尝一死  阅读(223)  评论(0编辑  收藏  举报