本文期待解决的问题:1、html中 <input id="input"type="file"/> 标签可以在android真机各个版本上调取本地文件,并获取文件信息。
第一次做的时候想到利用WebView的JS交互来完成。
思路:给WebView添加JS接口。
WebView.addJavascriptInterface(new JavascriptInterface(),"");在接口 JavascriptInterface中定义方法暴露给html调用传参。返回后在方法中实现想要的操作。如果有需要通知html界面的操作时,可以调用下面的方法传递参数。
WebView.loadUrl("javascript:hello_android('" + picIds + "','" + imageurl + "')");
上面的思路可以实现简单的JS交互,但有安全隐患。 so...不到实在没有办法了,不建议使用。
Webview的属性设置中提供了,打开本地文件的回调方法。
WebView.setWebChromeClient(new ReWebChomeClient(this));
这里的 ReWebChomeClient 继承了 extends WebChromeClient
在重写的方法中有针对android 各个版本打开本地文件的对应的方法。 废话少说,实践一下。
1、开发环境要求5.0以上(一些方法只有5.0下才能重写生效)。
2、这里的html放在本地assets资源文件下。
WebView = (WebView) findViewById(R.id.webview); WebView.setWebChromeClient(new ReWebChomeClient(this)); WebView.setWebViewClient(new ReWebViewClient()); //这里加载自己Url(也可加载本地资源) WebView.loadUrl("file:///android_asset/input.html");
设置WebView的基本属性。 接下来看看 new ReWebChomeClient(this) 中实现了什么。
继承了WebChromeClient 重写了4个方法。public class ReWebChomeClient extends WebChromeClient
// For Android < 3.0 public void openFileChooser(ValueCallback<Uri> uploadMsg)
//For Android 3.0+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType)
// For Android > 4.1.1 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
// For Android > 5.0 public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> uploadMsg, WebChromeClient.FileChooserParams fileChooserParams)从小于3.0到大于5.0的各个版本需要的方法。
由于5.0的重写方法中传递的参数 ValueCallBack<Uri[]> uploadMsg 与其他的几个方法传递参数不同,我们在自定义的接口中实现两个方法。在上面重写的方法中接收参数。
/** * 自定义接口 方便MainActivity调用 */ public interface OpenFileChooserCallBack { void openFileChooserCallBack(ValueCallback<Uri> uploadMsg, String acceptType); void openFileChooserCallBack(ValueCallback<Uri[]> uploadMsg,WebChromeClient.FileChooserParams fileChooserParams); }
ReWebChomeClient类完整代码:
public class ReWebChomeClient extends WebChromeClient { private OpenFileChooserCallBack mOpenFileChooserCallBack; public ReWebChomeClient(OpenFileChooserCallBack openFileChooserCallBack) { mOpenFileChooserCallBack = openFileChooserCallBack; } // For Android < 3.0 public void openFileChooser(ValueCallback<Uri> uploadMsg) { openFileChooser(uploadMsg, ""); } //For Android 3.0+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { mOpenFileChooserCallBack.openFileChooserCallBack(uploadMsg, acceptType); } // For Android > 4.1.1 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { openFileChooser(uploadMsg, acceptType); } // For Android > 5.0 public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> uploadMsg, WebChromeClient.FileChooserParams fileChooserParams) { mOpenFileChooserCallBack.openFileChooserCallBack(uploadMsg,fileChooserParams); return true; } /** * 自定义接口 方便MainActivity调用 */ public interface OpenFileChooserCallBack { void openFileChooserCallBack(ValueCallback<Uri> uploadMsg, String acceptType); void openFileChooserCallBack(ValueCallback<Uri[]> uploadMsg,WebChromeClient.FileChooserParams fileChooserParams); } }
接下来 new ReWebViewClient() 中没什么技术点。说句题外话。当WebView中有链接可以点击时,默认是跳到浏览器中浏览。而我们想在WebView中继续浏览时。重写
shouldOverrideUrlLoading方法。 就可以在webview中继续浏览。如果有电话号码可以点击 也可以在这个方法中去做判断,具体实现看下面代码。
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains("tel:")) {
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse(url));
startActivity(intent);
} else {
view.loadUrl(url);
}
return true;
}
举一反三,既然要打开系统拨号键盘,那邮箱,地图也可以支持
if (url.startsWith("mailto:") || url.startsWith("geo:") || url.startsWith("tel:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
接下来看 MainActivity中具体做哪些工作。
首先,实现自定义的接口 OpenFileChooserCallBack ,实现两个方法。当点击file标签时,回调成功。具体实现代码写在两个方法中。
implements ReWebChomeClient.OpenFileChooserCallBack
@Override public void openFileChooserCallBack(ValueCallback<Uri> uploadMsg, String acceptType) { mUploadMsg = uploadMsg; showOptions(); } @Override public void openFileChooserCallBack(ValueCallback<Uri[]> uploadMsg, WebChromeClient.FileChooserParams fileChooserParams) { mUploadMessageForAndroid5 = uploadMsg; showOptions(); }
public void showOptions() { CharSequence[] sequences = {"相册", "拍照"}; AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); alertDialog.setOnCancelListener(new ReOnCancelListener()); alertDialog.setTitle("选择图片"); alertDialog.setItems(sequences, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (which == 0) { Intent showImgIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(showImgIntent, REQUEST_CODE_PICK_IMAGE); } else { File dir = new File(Environment.getExternalStorageDirectory() + "/econ/"); if (!dir.exists()) { dir.mkdirs(); } File mCameraPicture = new File(dir, System.currentTimeMillis() + ".jpg"); Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mCameraPicture)); cameraIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); startActivityForResult(cameraIntent, REQUEST_CODE_IMAGE_CAPTURE); } } } ); alertDialog.show(); }
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != Activity.RESULT_OK) { if (mUploadMsg != null) { mUploadMsg.onReceiveValue(null); mUploadMsg = null; } if (mUploadMessageForAndroid5 != null) { mUploadMessageForAndroid5.onReceiveValue(null); mUploadMessageForAndroid5 = null; } return; } if (null == mUploadMessageForAndroid5) return; Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData(); if (result != null) { mUploadMessageForAndroid5.onReceiveValue(new Uri[]{result}); } else { mUploadMessageForAndroid5.onReceiveValue(new Uri[]{}); } mUploadMessageForAndroid5 = null; 。。。。。。。。。。 }
实现思路基本完成,说说在实现过程中遇到的问题。
1、支持release版
debug版是好的,为什么release就不行了呢?准确的说,开启了混淆的release包是不可以的,究其原因在于, openFileChooser
方法并不是WebChromeClient
的对外开放的方法,因此这个方法会被混淆,解决办法也比较简单,只需要在混淆文件里控制一下即可:
-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}
2、在操作过程中选完图片后返回或直接返回。发现选择文件按钮点击无反应!
ValueCallback<Uri> uploadMsg 参数的 onReceiveValue() 方法。重新置为null。选择文件按钮就可以重新被调用了。
①、 如果你的弹出选择框是自定义的,那就设置一个监听来重置。②、在onActivityResult方法中。
if (resultCode != Activity.RESULT_OK) { if (mUploadMsg != null) { mUploadMsg.onReceiveValue(null); mUploadMsg = null; } return; } 。。。。。有操作(选择了文件) 处理完数据别忘记将onReceiveValue(null)