原生:安卓 , 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) }) },
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);
}
}