前言:我在做WebView加载网页时,可能会出现未知的各种错误情况,所以在xml布局时,是不会直接把WebView控件写在xml布局中的额,为了防止对webview的引用无法断开,所以我在布局写一个布局容器,比如:LinearLayout ,然后在代码中动态的创建WebView控件,然后添加到容器布局LinearLayout中,然后在activity的生命周期中onDestory() 方法中把WebView移除销毁。不知道这样做是不是会更好一些。目前我是这么做的。
1、首先先定义全局控件:
private WebView webView;
private WebSettings webSettings;
private LinearLayout linearLayout;
private ProgressBar progress; //加一个原生的加载框,当网页重新加载的时候显示,加载完隐藏
ConnectivityManager connectivityManager; // 网络连接
private ValueCallback mUploadMessage; // 打开相册
private ValueCallback mUploadMessageForAndroid5; // 打开相册
private int FILECHOOSER_RESULTCODE = 111;
private int FILECHOOSER_RESULTCODE_FOR_ANDROID_5 = 115;
private int RESULT_OK = 0xA1;
private WebViewInterFace webViewInterFace;
2、在onCreate方法中初始化控件,但是WebView是没有的,动态创建添加
progress = findViewById(R.id.progress);
linearLayout = findViewById(R.id.linearLayout);
// 动态创建WebView 控件,设置宽高为MATCH_PARENT,添加webview到父布局的linearLayout中,
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
webView = new WebView(this);
webView.setLayoutParams(layoutParams);
linearLayout.addView(webView);
3、下面开始给WebView设置WebSetting了,这里设置的比较多,应为项目里面基本所有的功能都是以webview显示处理的
webSettings = webView.getSettings();
webView.setWebContentsDebuggingEnabled(true);
webSettings.setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET );
// 加载初始化登录的url页面
webView.loadUrl(webUrl);
webView.getSettings().setDomStorageEnabled(true); //设置DOM Storage缓存
webSettings.setJavaScriptEnabled(true); // 支持js脚本
webView.getSettings().setAppCacheMaxSize(1024*1024*8); // ----
String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();// ---
webView.getSettings().setAppCachePath(appCachePath); // ---
String ua = webSettings.getUserAgentString();
// 设置在本WebView中加载网页时添加网页的UserAgent的标识,用于在js中判断处理
webSettings.setUserAgentString(ua + ";operationApp");
webSettings.setAllowContentAccess(true); // 允许在WebView中访问内容URL
webSettings.setNeedInitialFocus(false); // 调用时是否需要设置节点以获得焦点
webSettings.setCacheMode(android.webkit.WebSettings.LOAD_NO_CACHE); // 设置缓存
webSettings.setPluginState(android.webkit.WebSettings.PluginState.ON);
webSettings.setAllowFileAccess(true); // 启用文件访问
webSettings.setAllowFileAccessFromFileURLs(false); // 允许js访问其他url的内容
webSettings.setAllowUniversalAccessFromFileURLs(false);
webSettings.setSavePassword(false);
webView.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
webView.requestFocusFromTouch();
webView.requestFocus();
webView.setFocusable(true);
webView.setLongClickable(true);
webSettings.setAppCacheEnabled(true); // 应用缓存API是否可用 结合setAppCachePath(String)使用
//是否使用内置的缩放机制
// 缩放机制包括屏幕上的缩放控件(浮于WebView内容之上)和缩放手势的运用。通过setDisplayZoomControls(boolean)可以控制是否显示这些控件
webSettings.setBuiltInZoomControls(true);
webSettings.setUseWideViewPort(true); // 是否支持HTML的“viewport”标签或者使用wide viewport
webSettings.setLoadWithOverviewMode(true); // 是否允许WebView度超出以概览的方式载入页面
webSettings.setDatabaseEnabled(true); // 设置是否启用数据库存储API
webSettings.setGeolocationEnabled(true); // 设置是否启用地理位置
webView.setWebChromeClient(new android.webkit.WebChromeClient()); // 处理解析,渲染网页等
// 创建和js交互的接口类,这里我把和js交互的接口类单独领出来写在一个类里面了,然后加了回调处理
webViewInterFace = new WebViewInterFace(this);
// 设置js和Android调用的绑定,js端通过 window.android. 加上Android端的接口类中方法即可调用
webView.addJavascriptInterface(webViewInterFace,"android");
//这里是一个处理webview的回调
webViewChromeClient();
webViewInterFace.setActionListener(this);
// 加快HTML网页加载完成速度
if (Build.VERSION.SDK_INT >= 19) {
webSettings.setLoadsImagesAutomatically(true);
} else {
webSettings.setLoadsImagesAutomatically(false);
}
4、webViewChromeClient() 方法
/**
* 网页加载情况处理
*/
private void webViewChromeClient() {
webView.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(android.webkit.WebView view, int newProgress) {
progress.setProgress(newProgress*100);
if (newProgress == 100){
// 当newProgress为100时表示当前页面加完完成,隐藏即可
progress.setVisibility(View.GONE);
}
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(android.webkit.WebView view, String title) {
super.onReceivedTitle(view, title);
// 获取当前当前网页的头部标题 title ,判断是否网页正常加载出来,加载失败时显示无内容界面,
// selectUrl 是一个定义的成员String 空字符串,为了处理js中类似跳转简写方式时,只写一个javscript 在这里会出问题。
if (selectUrl.equals("javscript")){
return;
}else {
selectUrl = "";
if (title.contains(getString(R.string.no_netWork))){
Util.showToast(getString(R.string.no_internet));
webView.setVisibility(View.GONE);
ll_noNetWork.setVisibility(View.VISIBLE);
}else {
webView.setVisibility(View.VISIBLE);
ll_noNetWork.setVisibility(View.GONE);
}
}
}
//For Android >= 4.1
public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) {
openFileChooserImpl(uploadMsg);
}
// For Android > 5.0
@Override
public boolean onShowFileChooser(android.webkit.WebView webView, ValueCallback uploadMsg, android.webkit.WebChromeClient.FileChooserParams fileChooserParams) {
openFileChooserImplForAndroid5(uploadMsg);
return true;
}
});
}
5、监听webview的加载状态 setWebViewLoad(); 这个方法是在websetting中设置完了之后直接就初始化这个方法
/**
* 设置webview加载状态
*/
private void setWebViewLoad() {
webView.setWebViewClient(new WebViewClient(){
/**
* 表示页面中的所有url点击跳转操作都由自己处理,不用手机系统自带的浏览器处理
* @
* view
* @param url
* @return
*/
@Override
public boolean shouldOverrideUrlLoading(android.webkit.WebView view, String url) {
if (url.equals("javscript:;")){
selectUrl = "javscript";
}else
view.loadUrl(url);
return true;
}
/**
* 加载页面之前调用
* @param view
* @param url
* @param favicon
*/
@Override
public void onPageStarted(android.webkit.WebView view, String url, Bitmap favicon) {
// 在这里进行显示正在加载的Dialog
progress.setVisibility(View.VISIBLE);
// 判断当前是否有可用网络,有网络时从网络获取,无网络时
final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo == null || !networkInfo.isAvailable()){
// 表示无网络 根据cache-control决定是否从网络上取数据
webView.getSettings().setCacheMode(android.webkit.WebSettings.LOAD_DEFAULT);
Util.showToast(getString(R.string.no_internet));
}else {
// 表示有网络 只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据
webView.getSettings().setCacheMode(android.webkit.WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
super.onPageStarted(view, url, favicon);
}
/**
* 页面加载结束调用
* @param view
* @param url
*/
@Override
public void onPageFinished(android.webkit.WebView view, String url) {
// 在这里进行隐藏正在加载的Dialog
progress.setVisibility(View.GONE);
super.onPageFinished(view, url);
}
/**
* 请求网络资源的时候都会调用
* @param view
* @param url
*/
@Override
public void onLoadResource(android.webkit.WebView view, String url) {
super.onLoadResource(view, url);
}
});
}
6、openFileChooserImpl(uploadMsg) 方法和 openFileChooserImplForAndroid5(uploadMsg); 这是js里面有打开手机相册,获取照片时需要的
private void openFileChooserImpl(ValueCallback uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
// 跳转手机相册
private void openFileChooserImplForAndroid5(ValueCallback uploadMsg) {
mUploadMessageForAndroid5 = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILECHOOSER_RESULTCODE_FOR_ANDROID_5);
}
7、对js获取手机照片的回调
/**
* 对获取照片结果的回调
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// 选择图片后的回调
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {
// 选择图片后一般执行此条件
if (null == mUploadMessageForAndroid5)
return;
// 获取返回的照片uri地址
Uri result ;
if (data != null && data.getData() != null){
result = data.getData();
}else {
return;
}
if (data != null) {
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{result});
} else {
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{});
}
mUploadMessageForAndroid5 = null;
}
}
8、重写返回键事件,用于捕获在网页上返回时,直接关闭页面的情况,
/**
* 重写onKeyDown 处理手机返回键按下事件
* 如何按下返回键时webview可以返回上一个页面就执行返回上一个页面
* 当回到主界面时,点击一次返回提示,点击两次退出程序
* @param keyCode
* @param event
* @return
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
webView.goBack();// 返回前一个页面
return true;
}else {
// clickNum 初始化为 0 ,就是为了判断点击返回两次,就关闭当前activity,
clickNum ++ ;
if (clickNum < 2){
Toast.makeText(YWWebAllActivity.this,getString(R.string.quit),Toast.LENGTH_SHORT).show();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
9、onDestory() 处理webview
/**
* 运行销毁onDestroy时清除webview所占的内存
*/
@Override
protected void onDestroy() {
if (webView != null){
ViewParent parent = webView.getParent();
if(parent != null){
((ViewGroup)parent).removeView(webView);
}
webView.stopLoading();
webView.getSettings().setJavaScriptEnabled(false);
webView.clearHistory();
webView.clearView();
webView.removeAllViews();
webView.destroy();
}
super.onDestroy();
}
10、和js交互的接口类,单独写在一个类里面,避免activity写太多了,这里需要加什么调用的方法,可以随便加,方法上加上@JavascriptInterface就行了
/**
* WebView 和App端接口类
*/
public class WebViewInterFace {
private Context context;
public WebViewInterFace(Context context) {
this.context = context;
}
/**
* 提供给js调用获取用户登录信息方法
* @return
*/
@JavascriptInterface
public String getUserInfo(){
final String userInfo = MySharedPreferenceUtil.getUserLoginInfoJson();
return userInfo;
}
/**
* 提供给js调用,获取token
* @return
*/
@JavascriptInterface
public String getToken(){
String token = MySharedPreferenceUtil.getWebToken();
return token;
}
/**
* 提供给js保存登录信息方法
*/
@JavascriptInterface
public void saveInfo(String data,int status) {
// 保存js调用传递过来的数据data,
}
}
在多加一个,上面介绍js调用android,这里加一个android调用js方法:
//androidCallBack() 为js中写的function方法。前面的javascript: 为固定写法,如果想向js传递数据,可在androidCallBack()括号中添加参数,用字符串拼接的方法。
// 比如:webView.loadUrl("javascript:androidCallBack('"+ 参数 +"')");
webView.loadUrl("javascript:androidCallBack()");
好了上面就是Android中WebView和js交互的情况了。