在安卓开发过程中,会出现对接H5需要拍照及选择本地图片上传的需求。这个功能的实现需要调用安卓的相关操作,然后将获取到的图片路径传给H5。
input
标签中type="file"
可用于文件上传,accept="image/*"
表示上传的类型为Image类型,点击可通知安卓执行上传操作
<input type="file"
accept="image/*"
id="inputimg"/>
H5通过img
标签展示安卓端传过来的图片
<img id="image" style="width: 300px; height: 300px;">
在JavaScript中执行接收操作
<script type="text/javascript">
var input = document.getElementById("inputimg");
var image = document.getElementById("image");
input.onchange = function () {
var file = this.files[0];
var url = URL.createObjectURL(file);
image.src=url;
}
</script>
WebView中通过重写WebChromeClinet
的onShowFileChooser
或openFileChooser
方法来实现文件的上传
onShowFileChooser是5.0以上版本使用的,openFileChooser是4.1以上版本使用,但两个版本的API均有
ValueCallback
参数,通过调用ValueCallback参数的onReceiveValue
方法即可将选择图片的Uri传给H5。
因为两个API的实现方式类似,这里只介绍5.0以上的实现方法,不再介绍4.1+的API实现。
// 5.0+
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePathCallback;
Intent intent = gotoChooseFile(); // 选择文件及拍照
startActivityForResult(intent, REQUEST_CODE_LOLIPOP);
return true;
}
// 4.1+
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
}
使用系统自带的选择器弹出拍照或选择图片弹框,选中需要上传的图片后回到当前页面。
**注意事项:**
1、安卓6.0+的设备需要
动态添加拍照及读写内存的权限
,具体实现不再展示;
2、获取图片intent部分的ACTION_GET_CONTENT
也可以用ACTION_PICK
的方法代替。但不管使用ACTION_GET_CONTENT还是ACTION_PICK,在不同系统的手机上表现不同,例如:在6.0手机上,可能会出现相机、相册、文件管理,如果安装了其他相机,也会出现其他相机的选项;在5.x设备上,可能展示为相机、文档…
/**
* 选择文件及拍照
*/
private Intent gotoChooseFile() {
String saveName = Environment.getExternalStorageDirectory().getPath() + "/" + Environment.DIRECTORY_DCIM + "/Camera/";
/**
* 打开相机intent
*/
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(this.getPackageManager()) != null) {
File photoFile = null;
photoFile = new File(saveName + randomFileName() + ".jpg");
if (!photoFile.exists()) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile)); // 把Uri赋值给takePictureIntent
} else {
takePictureIntent = null;
}
}
Intent[] takeoutArray = null;
if (takePictureIntent != null) {
takeoutArray = new Intent[]{takePictureIntent};
} else {
takeoutArray = new Intent[0];
}
/**
* 获取图片intent
*/
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
/**
* 使用系统选择器
*/
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, takeoutArray); // 额外的intent
return chooserIntent;
}
/**
* 随机产生文件名
*/
private String randomFileName() {
return UUID.randomUUID().toString();
}
在onActivityResult中将从拍照或者文件中获取的图片Uri传递给H5
若没有选中图片而是点击了空白区域,则onReceiveValue一定要传null,否则无法再次唤起选择器。
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_LOLIPOP) { // 选择文件返回 5.0+
Uri[] results = null;
if (resultCode == RESULT_OK) {
if (data == null) {
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
mFilePathCallback.onReceiveValue(results); // 当获取要传图片的Uri,通过该方法回调通知
mFilePathCallback = null;
}
}
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<html>
<body>
<input type="file"
accept="image/*"
id="inputimg"/>
<img id="image" style="width: 300px; height: 300px;">
body>
<script type="text/javascript">
var input = document.getElementById("inputimg");
var image = document.getElementById("image");
input.onchange = function () {
var file = this.files[0];
var url = URL.createObjectURL(file);
image.src=url;
}
script>
html>
public class MainActivity extends AppCompatActivity {
private WebView mWebView = null;
private ValueCallback<Uri[]> mFilePathCallback = null;
private int REQUEST_CODE_LOLIPOP = 1; // 5.0以上版本
private String mCameraPhotoPath = ""; // 拍照的图片路径
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_webview);
mWebView = (WebView) findViewById(R.id.base_web_view);
showWebView();
}
@SuppressLint({"SetJavaScriptEnabled"})
private void showWebView() {
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.loadUrl("file:///android_asset/test2.html");
showCustomWebChromeClient();
}
private void showCustomWebChromeClient() {
mWebView.setWebChromeClient(new WebChromeClient() {
// 5.0+
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePathCallback;
Intent intent = gotoChooseFile(); // 选择文件及拍照
startActivityForResult(intent, REQUEST_CODE_LOLIPOP);
return true;
}
// 4.1+
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
}
});
}
/**
* 选择文件及拍照
*/
private Intent gotoChooseFile() {
String saveName = Environment.getExternalStorageDirectory().getPath() + "/" + Environment.DIRECTORY_DCIM + "/Camera/";
/**
* 打开相机intent
*/
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(this.getPackageManager()) != null) {
File photoFile = null;
photoFile = new File(saveName + randomFileName() + ".jpg");
if (!photoFile.exists()) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile)); // 把Uri赋值给takePictureIntent
} else {
takePictureIntent = null;
}
}
Intent[] takeoutArray = null;
if (takePictureIntent != null) {
takeoutArray = new Intent[]{takePictureIntent};
} else {
takeoutArray = new Intent[0];
}
/**
* 获取图片intent
*/
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
/**
* 使用系统选择器
*/
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, takeoutArray); // 额外的intent
return chooserIntent;
}
/**
* 随机产生文件名
*/
private String randomFileName() {
return UUID.randomUUID().toString();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_LOLIPOP) { // 选择文件返回 5.0+
Uri[] results = null;
if (resultCode == RESULT_OK) {
if (data == null) {
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
mFilePathCallback.onReceiveValue(results); // 当获取要传图片的Uri,通过该方法回调通知
mFilePathCallback = null;
}
}
@Override
protected void onDestroy() {
mWebView.removeAllViews();
mWebView.destroy();
super.onDestroy();
}
}
可在这里下载WebView实现拍照及选择图片的demo,如果6.0以上版本demo拍照异常,看一下应用管理中拍照及存储权限是否没有打开。最好在代码中加上动态设置权限的部分。