Android WebView调用系统相册和相机,注入以及与H5的交互
推荐:
如何设计一个优雅健壮的Android WebView?(上)
如何设计一个优雅健壮的Android WebView?(下)
Android Webview在使用的过程中,其实也是挺简单的,最基本的只需要加Seeting的配置即可成为一个H5链接的承载,但是不乏在使用的会遇到一些坑,这篇文章主要总结的是我在项目中所使用的一下Webview的功能等。各位大神,如果写的不正确的地方,请提出来!
一.Webview的基本配置
Webview的WebSettings基本配置大概也就那么多,当然了也有其他的少用的配置,这个可以根据需求来定,具体的每一个配置属性有注释,其他的属性大家可以在网上都能查到。我所使用的如下:
public void setWebSettings(WebView mView){
WebSettings setting = mView.getSettings();
//支持Js
setting.setJavaScriptEnabled(true);
setting.setJavaScriptCanOpenWindowsAutomatically(true);
//缓存模式
setting.setCacheMode(WebSettings.LOAD_DEFAULT);
//支持内容重新布局
setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
//将图片调整到适合webview的大小
setting.setUseWideViewPort(true);
setting.setLoadWithOverviewMode(true);
//设置可以访问文件
setting.setAllowFileAccess(true);
//支持自动加载图片
setting.setLoadsImagesAutomatically(true);
try {
setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
} catch (NoSuchMethodError e) {
e.printStackTrace();
}
}
对于Webview中的两个Client(WebViewClient和WebChromeClient)的配置,这里我也不在简述,毕竟方法太多了,在接下来的书写中会有一部分的描述使用。
二.Webview调用系统相册和相机
- 所需要的权限(注意针对于6.0系统,需要申请权限)
android.Manifest.permission.CAMERA)
android.Manifest.permission.READ_EXTERNAL_STORAGE
- 继承mWebChromeClient,对于系统相机和相册都是通过表单数据来返回给webview的,这里注意对比5.0以上和以下系统所是使用的方法,以下所有的代码都是对相机和相册的使用封装
public class BaseWebChromeClient extends WebChromeClient {
private final static int FILE_REQUEST_CODE_SNAPSHOT = 100; //拍照表单的结果回调
private final static int FILE_CHOOSER_RESULT_CODE = 101;// 相册表单的结果回调
private ValueCallback<Uri> mUploadMessage;// 5.0以下表单的数据信息
public ValueCallback<Uri[]> mUploadCallbackAboveL;//5.0以上表单的数据信息
private Uri imageUri;
private Context mContext;
public BaseWebChromeClient(Context context) {
mContext = context;
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (newProgress == 100) {
((BaseActivity) mContext).dismissLoadingDialog();
}
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
//处理404错误 android 6.0 以下通过title获取
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
if (title.contains("404") || title.contains("500") || title.contains("Error")) {
if (mContext instanceof BaseWebActivity) {
((BaseWebActivity) mContext).showErrorRefresh(view, mContext.getResources().getString(R.string.webview_not_found));
}
}
}
}
//<---------------------------------重要代码-------------------------------------------->
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
mUploadCallbackAboveL = filePathCallback;
PictureBusiness.getInstance().setUploadCallbackAboveL(mUploadCallbackAboveL);
String[] fileParams = fileChooserParams.getAcceptTypes();
if (fileParams.length > 0) {
for (int i = 0; i < fileParams.length; i++) {
LogUtil.i("BaseWebChromeClient", "FileChooserParams ----> " + fileParams[i]);
}
}
if (fileParams[0].equals(PictureBusiness.IMAGE)) {
PictureBusiness.getInstance().changePicture((Activity) mContext, PictureBusiness.IMAGE, false);
} else if (fileParams[0].contains(PictureBusiness.VIDEO)) {
PictureBusiness.getInstance().changePicture((Activity) mContext, PictureBusiness.VIDEO, false);
}
return true;
}
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUploadMessage = uploadMsg;
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
}
return;
}
//相册 & 拍照
if (requestCode == FILE_REQUEST_CODE_SNAPSHOT || requestCode == FILE_CHOOSER_RESULT_CODE) {
if (null == mUploadMessage && null == mUploadCallbackAboveL) {
return;
}
Uri result = data == null || (resultCode != RESULT_OK) ? null : data.getData();
if (mUploadCallbackAboveL != null) {
onActivityResultAboveL(requestCode, resultCode, data);
} else if (mUploadMessage != null) {
/*if (result != null) {
String path = getPath(mContext.getApplicationContext(), result);
Uri uri = Uri.fromFile(new File(path));
mUploadMessage.onReceiveValue(uri);
} else {
mUploadMessage.onReceiveValue(imageUri);
}
mUploadMessage = null;*/
}
}
}
@SuppressWarnings("null")
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
if (mUploadCallbackAboveL == null) {
return;
}
Uri[] results = null;
switch (requestCode) {
case FILE_CHOOSER_RESULT_CODE:
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
results = new Uri[]{imageUri};
} else {
String dataString = data.getDataString();
ClipData clipData = data.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
break;
case FILE_REQUEST_CODE_SNAPSHOT:
if (resultCode == Activity.RESULT_OK) {
String videoPath = null;
File capture = PictureBusiness.getInstance().getSnapshot();
if (capture != null && capture.exists()) {
videoPath = capture.getAbsolutePath();
}
if (videoPath != null) {
results = new Uri[]{Uri.parse("file://" + videoPath)};
}
}
break;
default:
break;
}
if (results != null) {
mUploadCallbackAboveL.onReceiveValue(results);
mUploadCallbackAboveL = null;
} else {
results = new Uri[]{imageUri};
mUploadCallbackAboveL.onReceiveValue(results);
mUploadCallbackAboveL = null;
}
return;
}
}
我在使用的过程中基本上是 没有问题的,但是部分手机上显示到webivew中的时候图片会有反转,这个也没有来的急解决,各位大佬有改过的可以评论一下方法
3.代码中使用
①在onResume中做判断
if (mWebChromeClient.mUploadCallbackAboveL != null) {
mWebChromeClient.mUploadCallbackAboveL.onReceiveValue(null);
mWebChromeClient.mUploadCallbackAboveL = null;
}
②重写onActivityResult方法
mWebChromeClient.onActivityResult(requestCode, resultCode, data);
三.使用Webview的拦截方法 shouldOverrideUrlLoading (重定向)
shouldOverrideUrlLoading :这个方法的返回值
return true 表示当前url即使是重定向url也不会再执行(除了在return true之前使用webview.loadUrl(url)除外,因为这个会重新加载)
return false 表示由系统执行url,直到不再执行此方法,即加载完重定向的ur(即具体的url,不再有重定向)
这个拦截方法其实也可以作为和H5的交互,比如在我自已的项目需求中,有一个按钮需要点击跳转,这个时候将返回设置为true,通过H5返回的数据(协商定义)进行判断跳转处理
四.解析Webview的Cookie
获取Cookie使用的是CookieManager,这里我举例项目中使用Cookie,如下代码:
@Override
protected void shouldUrlLoading(WebView view, String request) {
if (request.contains("backpwd") || request.contains("regagreement") || request.contains("problem/a5.html")) {
mBaseWeb.loadUrl(request);
} else {
//登录成功后,拦截操作
try {
LogUtil.i("login", URLDecoder.decode(request, "utf-8"));
if (URLDecoder.decode(request, "utf-8").contains(boLogin.getRedirect_url())) {
//------使用代码 start-------
CookieManager cookieManager = CookieManager.getInstance();
String cookieStr = cookieManager.getCookie(url);
LoginCookie cookie = getLoginCookie(cookieStr);
//------使用代码 end-----
LoginBean loginBean = new LoginBean();
loginBean.setUid(cookie.getBfuid());
loginBean.setThirdUid("");
loginBean.setToken(cookie.getLoginToken());
loginBean.setuName(cookie.getBf_user_name());
loginBean.setLoginType(cookie.getLogin_type());
LoginBusiness.login(this, loginBean);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
getLoginCookie方法如下:
/**
* 解析Cookie
*
* @param cookieStr
* @return
*/
protected LoginCookie getLoginCookie(String cookieStr) {
HashMap<String, String> cookieContiner = new HashMap<>();
String[] keyvalues = cookieStr.split(";");
for (int i = 0; i < keyvalues.length; i++) {
String[] keyPair = keyvalues[i].split("=");
String cookieKey = keyPair[0].trim();
String cookieValue = keyPair.length > 1 ? keyPair[1].trim() : "";
cookieContiner.put(cookieKey, cookieValue);
}
String cookie = JSONObject.toJSONString(cookieContiner);
LogUtil.i("cookie", "login cookie = " + cookieStr + "\n" + cookie);
LoginCookie loginCookie = JSONObject.parseObject(cookie, LoginCookie.class);
return loginCookie;
}
以上只是举个例子,具体H5返回的Cookie需要由后端来定
五.Webview的注入
关于Webview的注入我了解的有两种,也是最常用的,即H5调用Android的方式和Android调用H5的方式
- Android调用H5的方式
一般我们加载网页的是通过mWeb.loadUrl(url);加载的,这个其实也可以调用H5提供的方法,既能表示也能注入也能表示调用H5的方式,具体一行代码搞定:webView.loadUrl("javascript:"+"方法名");
- H5调用Android的方式
写一个类,加注解@JavascriptInterface,如下
@SuppressLint("JavascriptInterface")
class JSInterface {
/**
* Toast
*
* @param mToast 显示内容
*/
@JavascriptInterface
public void toast(String mToast) {
SRLog.i(TAG, "--->toast " + mToast);
Toast.makeText(WebActivity.this, mToast, Toast.LENGTH_SHORT).show();
}
/**
* 中间件 消息控制
*
* @param str 内容
*/
@JavascriptInterface
public void startMiddleWare(String str) {
SRLog.i(TAG, "--->middle " + str);
TellManager.getInstance().sendAsr(getApplication(), getPackageName(), str);
finish();
}
注意,WebSettings中的属性setJavaScriptEnabled(true);要设置为true
使用方式:
JSInterface mJsInterface = new JSInterface(this);
mBaseWeb.addJavascriptInterface(mJsInterface, "InjectOsUser");
//InjectOsUser这个是需要提供给H5调用的,如InjectOsUser.toast("xxx");
六.总结
以上就是WebView的部分使用,这也是有可能大部分项目中能使用到的功能,这里我也不在叙述WebViewClient中的一些方法,如onPageStarted,onPageFinished等,这些方法的使用也比较简单。最后在推荐一个对WebView的使用非常全的网站,大家可以在上面查到更多的方法。
如何设计一个优雅健壮的Android WebView?(上)
如何设计一个优雅健壮的Android WebView?(下)