参考:https://blog.csdn.net/qq_36252044/article/details/77993297
https://blog.csdn.net/lowprofile_coding/article/details/77928614
https://blog.csdn.net/carson_ho/article/details/64904691/ 个人认为写的最好的一个
1.WebView介绍
Android WebView在Android平台上是一个特殊的View, 基于webkit引擎、展现web页面的控件,这个类可以被用来在你的app中仅仅显示一张在线的网页,还可以用来开发浏览器。WebView内部实现是采用渲染引擎来展示view的内容,提供网页前进后退,网页放大,缩小,搜索。Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。
现在很多APP都内置了Web网页,比如说很多电商平台,淘宝、京东、聚划算等等。WebView比较灵活,不需要升级客户端,只需要修改网页代码即可。一些经常变化的页面可以用WebView这种方式去加载网页。例如中秋节跟国庆节打开的页面不一样,如果是用WebView显示的话,只修改修改html页面就行,而不需要升级客户端。
Webview功能强大,可以直接使用html文件(本地sdcard/assets目录),还可以直接加载url,使用JavaScript可以html跟原生APP互调。
2.加载html四种方式
webView.loadUrl("http://139.196.35.30:8080/OkHttpTest/apppackage/test.html");//加载url
webView.loadUrl("file:///android_asset/test.html");//加载asset文件夹下html
//方式3:加载手机sdcard上的html页面
webView.loadUrl("content://com.ansen.webview/sdcard/test.html");
//方式4 使用webview显示html代码
webView.loadDataWithBaseURL(null,"<html><head><title> 欢迎您 </title></head>" +
"<body><h2>使用webview显示 html代码</h2></body></html>
3.WebSettings的一些配置
WebSettings settings = wv.getSettings();
//允许使用js
settings.setJavaScriptEnabled(true);
//允许JS弹窗
settings.setJavaScriptCanOpenWindowsAutomatically(true);
//支持屏幕缩放
settings.setSupportZoom(true);
settings.setBuiltInZoomControls(true);
//不显示缩放按钮
settings.setDisplayZoomControls(false);
//设置自适应屏幕,两者合用
//将图片调整到适合webview的大小
settings.setUseWideViewPort(true);
// 缩放至屏幕的大小
settings.setLoadWithOverviewMode(true);
/**
* LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
* LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
* LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
* LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
*/
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
//设置可以访问文件
settings.setAllowFileAccess(true);
//支持自动加载图片
settings.setLoadsImagesAutomatically(true);
//设置编码格式
settings.setDefaultTextEncodingName("utf-8");
4.我的代码
Android的:
package com.ysl.myandroidbase.activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.JavascriptInterface;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.Toast;
import com.ysl.myandroidbase.R;
public class WebViewActivity extends AppCompatActivity {
public static final String TAG = "WebViewActivity";
private WebView wv;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
button = findViewById(R.id.b);
wv = findViewById(R.id.wv);
WebSettings settings = wv.getSettings();
//允许使用js
settings.setJavaScriptEnabled(true);
//允许JS弹窗
settings.setJavaScriptCanOpenWindowsAutomatically(true);
//支持屏幕缩放
settings.setSupportZoom(true);
settings.setBuiltInZoomControls(true);
//不显示缩放按钮
settings.setDisplayZoomControls(false);
//设置自适应屏幕,两者合用
//将图片调整到适合webview的大小
settings.setUseWideViewPort(true);
// 缩放至屏幕的大小
settings.setLoadWithOverviewMode(true);
/**
* LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
* LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
* LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
* LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
*/
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
//设置可以访问文件
settings.setAllowFileAccess(true);
//支持自动加载图片
settings.setLoadsImagesAutomatically(true);
//设置编码格式
settings.setDefaultTextEncodingName("utf-8");
wv.loadUrl("http://www.baidu.com");
//加载asset文件夹下html
// 格式规定为:file:///android_asset/文件名.html
// wv.loadUrl("file:///android_asset/testJS.html");
//使用webview显示html代码
// wv.loadDataWithBaseURL(null,"<html><head><title> 欢迎您 </title></head>" +
// "<body><h2>使用webview显示 html代码</h2></body></html>", "text/html" , "utf-8", null);
//添加js监听 这样html就能调用客户端
// 通过addJavascriptInterface()将Java对象映射到JS对象
//参数1:Javascript要调用的Java对象
//参数2:Java对象名,这个名字可以任意起,但要和js调用的地方一致
wv.addJavascriptInterface(this,"android");
//WebViewClient主要帮助WebView处理各种通知、请求事件
wv.setWebViewClient(new WebViewClient(){
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.d(TAG,"onPageStarted--->开始加载了--->"+url);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.d(TAG,"onPageFinished--->加载结束了--->"+url);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.d(TAG,"shouldOverrideUrlLoading--->url--->"+url);
if(url.equals("http://www.google.com/")){
Toast.makeText(WebViewActivity.this,"国内不能访问google,拦截该url",Toast.LENGTH_LONG).show();
return true;//表示我已经处理过了
}
//true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器。默认false。
try {
if(url.startsWith("http:") || url.startsWith("https:") ) {
view.loadUrl(url);
}else{
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
});
//WebChromeClient主要辅助WebView处理Javascript的对话框、网站图标、网站title、加载进度等
wv.setWebChromeClient(new WebChromeClient(){
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
Log.d(TAG,"onReceivedTitle--->获取到网页的标题--->"+title);
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(TAG,"onJsAlert--->获取到JS--->"+message);
AlertDialog.Builder localBuilder = new AlertDialog.Builder(wv.getContext());
localBuilder.setTitle("Alert")
.setMessage(message)
.setNegativeButton("取消",null)
.setPositiveButton("确定",null)
.setCancelable(false)
.create()
.show();
//注意:
//必须要这一句代码:result.confirm()表示:
//处理结果为确定状态同时唤醒WebCore线程
//否则不能继续点击按钮
result.confirm();
//返回true,拦截系统的alert提示;
return true;
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
@Override
public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
return super.onJsBeforeUnload(view, url, message, result);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
Log.d(TAG,"onProgressChanged--->正在加载中--->"+newProgress);
}
});
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
wv.loadUrl("javascript:callJS()");
} else {
wv.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
System.out.println("value -----------------》 "+value);
Toast.makeText(WebViewActivity.this, "evaluateJavascript:"+value, Toast.LENGTH_SHORT).show();
}
});
}
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.d(TAG,"是否有上一个页面:"+wv.canGoBack());
if (wv.canGoBack() && keyCode == KeyEvent.KEYCODE_BACK){//点击返回按钮的时候判断有没有上一页
wv.goBack(); // goBack()表示返回webView的上一页面
return true;
}
return super.onKeyDown(keyCode,event);
}
@Override
protected void onDestroy() {
if(wv != null){
wv.clearHistory();
((ViewGroup)wv.getParent()).removeView(wv);
wv.destroy();
wv = null;
}
super.onDestroy();
}
/**
* JS调用android的方法
*/
@JavascriptInterface
public void getClient(String str){
Log.d(TAG,"html调用客户端:"+str);
Toast.makeText(WebViewActivity.this, str, Toast.LENGTH_SHORT).show();
}
}
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>这是标题啊</title>
<style type="text/css">
dd {
margin-top:30px; /* 上外边距30像素 */
}
</style>
</head>
<body>
<div id="wrap">
<div id="header"><h1>Webview简单使用</h1>
</div>
<div id="main">
<dl>
<dd><a href="http://www.baidu.com">点击跳转到百度</a></dd>
<dd><a href="https://www.jd.com">点击跳转到google</a></dd>
<!--<dd>
<button id='callback_client1' onclick="callBackClient1()" type="button">用js调用alert
</button>
</dd>-->
<dd>
<button id='callback_client2' onclick="callAndroid()" type="button">用js调用android
</button>
</dd>
</dl>
</div>
</div>
</body>
<script>
function callJS(){
alert("alert android调用js");
return "返回android调用js";
}
function callAndroid(){
javascript:android.getClient("js调用android");
}
</script>
</html>
5.遇到的错误:
错误一:
如上图错误,加载不了任何的网页。解决需要在清单文件application标签下添加:android:usesCleartextTraffic="true"
错误二:
这是由于webview只能识别简单的scheme,如“http”和“https”。像自定义的这些是不能加载的,需要我们重新去定义url.
在设置的WebViewClient的回调方法shouldOverrideUrlLoading方法中可以来做一些修改。
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.d(TAG,"shouldOverrideUrlLoading--->url--->"+url);
//true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器。默认false。
try {
if(url.startsWith("http:") || url.startsWith("https:") ) {
view.loadUrl(url);
}else{
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
错误三:
出现了两个弹窗,第一个在上面,第二个在下面。这是由于在WebChromeClient的回调方法onJsAlert中没有返回true或false,而是使用了父类的返回值。修改:
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(TAG,"onJsAlert--->获取到JS--->"+message);
AlertDialog.Builder localBuilder = new AlertDialog.Builder(wv.getContext());
localBuilder.setTitle("Alert")
.setMessage(message)
.setNegativeButton("取消",null)
.setPositiveButton("确定",null)
.setCancelable(false)
.create()
.show();
//注意:
//必须要这一句代码:result.confirm()表示:
//处理结果为确定状态同时唤醒WebCore线程
//否则不能继续点击按钮
result.confirm();
//返回true,拦截系统的alert提示;
return true;
}
6.注意点
对于Android调用JS代码的方法有2种:
1. 通过WebView
的loadUrl()
JS代码调用一定要在 onPageFinished()
回调之后才能调用,否则不会调用。
2. 通过WebView
的evaluateJavascript()
Android 4.4 后才可使用
所以,最终的写法是这样的:
// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
wv.loadUrl("javascript:callJS()");
} else {
wv.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
System.out.println("value -----------------》 "+value);
Toast.makeText(WebViewActivity.this, "evaluateJavascript:"+value, Toast.LENGTH_SHORT).show();
}
});
}
对于JS调用Android代码的方法有3种:
1. 通过WebView的addJavascriptInterface()进行对象映射
注意点:(存在严重的漏洞问题,具体请看文章:你不知道的 Android WebView 使用漏洞)
//添加js监听 这样html就能调用客户端
// 通过addJavascriptInterface()将Java对象映射到JS对象
//参数1:Javascript要调用的Java对象
//参数2:Java对象名,这个名字可以任意起,但要和js调用的地方一致
wv.addJavascriptInterface(this,"android");
2. 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
后两种方式请参考:Android:你要的WebView与 JS 交互方式 都在这里了