Android之WebView快速上手

144
作者 absfree
2016.09.01 22:01* 字数 1217

什么是WebView

WebView用于展示Web页面,使用它我们可以很方便在我们的应用中显示网页。WebView使用WebKit渲染引擎来显示Web页面,同时它包含了前进/后退,放大/缩小以及进行文本搜索等方法。(从Android 4.4开始,WebView使用Chromium作为内核) 若我们想在应用中使用WebView来加载网页,需要在AndroidManifest.xml添加网络访问权限:

<uses-permission android:name="android.permission.INTERNET" />

WebViewClient

WebViewClient和下面将要提到的WebChromeClient的功能都是帮助WebView分担一些工作,这样一来WebView就只需专注于自己的加载网页的工作,而加载网页过程中所需处理的一些其他事宜便交给WebViewClient和WebChromeClient去处理。这种设计一定程度上体现了单一职责原则。
WebViewClient的主要职责是帮助WebView处理各种通知、回调事件。比如在点击WebView中网页的链接时,默认行为是打开系统浏览器,而我们想要直接由WebView响应对链接的点击,这时我们可以为WebView设置一个自定义WebViewClient并重写shouldOverrideUrlLoading方法。
再比如WebViewClient类中有如下几个方法:

//当WebView的缩放因子改变时这个方法会被回调
public void onScaleChanged (WebView  view, float oldScale, float newScale)
//当网页加载完毕后这个方法会被回调
public void onPageFinished (WebView view, String url)
//当网页开始加载时这个方法会被回调
public void onPageStarted (WebView view, String url, Bitmap favicon)

WebChromeClient

WebChromeClient的作用是监听网页加载进度以及对网页标题,网页图标、JS对话框进行处理等等。

比如WebChromeClient的onProgressChanged方法监听网页的加载进度:

public void onProgressChanged(WebView view, int newProgress) { 
  //newProgress为当前最新加载进度
}

WebChromeClient的onReceivedTitle方法中可获取到网页标题:

public void onReceivedTitle(WebView view, String title) { 
   //title为网页标题,在获取失败时则为“找不该网页”
}

onReceivedIcon方法中则可以获取到网页图标:

@Override 
public void onReceivedIcon(WebView view, Bitmap icon) { 
  //icon为网页图标 
}

WebChromeClient的以下三个方法用于处理JS对话框:

//处理alert对话框
@Override 
public boolean onJsAlert(WebView view, String url, 
    String message, JsResult result) { 
  . . . 
} 
//处理confirm对话框
@Override 
public boolean onJsPrompt(WebView view, String url, 
    String message, String defaultValue, JsPromptResult result) { 
  . . . 
} 
//处理prompt对话框 
@Override 
public boolean onJsConfirm(WebView view, String url, 
    String message, JsResult result) { 
  . . . 
}

基本使用

编辑res/layout/activity_main.xml内容如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout   
    xmlns:android="http://schemas.android.com/apk/res/android"                 
    android:layout_width="match_parent"                 
    android:layout_height="match_parent">    
  <WebView        
      android:layout_width="match_parent"            
      android:layout_height="match_parent"        
      android:id="@+id/wv_demo" />
</FrameLayout>

然后在MainActivity中:

public class MainActivity extends Activity {    
  private WebView mWebView;    

  @Override    
  protected void onCreate(Bundle savedInstanceState) {        
    super.onCreate(savedInstanceState);        
    mWebView = (WebView) findViewById(R.id.wv_demo);        
    /** 加载本地html
       * local.html存放于assets目录下
       * mWebView.loadUrl("file:///android_asset/local.html");
    */        
    mWebView.loadUrl("http://www.jianshu.com");    
  }
}

支持JavaScript

若要加载的网页中含有JavaScript脚本,则必须开启对JavaScript的支持:

WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

WebView自己响应链接

默认情况下,我们在WebView加载的网页中点击一个链接,系统会打开自带浏览器来响应这个链接。若想由WebView自身响应而不打开系统自带浏览器,只需进行如下设置:

mWebView.setWebViewClient(new WebViewClient() {
  public boolean shouldOverrideUrlLoading(WebView view,String url) { 
    view.loadUrl(url); 
    return true; 
  }
});

WebView的页面导航

默认情况下,我们在WebView界面点击Back键,WebView的finish方法会被调用从而结束自己。若我们想实现点击Back键能够实现浏览器的后退功能,只需重写onKeyDown方法:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) { 
  if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) { 
    mWebView.goBack(); 
    return true; 
  } 
  return super.onKeyDown(keyCode, event);
}

上面代码中的canGoBack方法用于判断历史记录中是否存在曾经访问过的页面,goBack方法用于后退到刚刚访问过的页面。

WebView缓存

若我们的应用使用了WebView,则会在"/data/data/<应用包名>"目录下生成 database 与 cache 两个文件夹。
是否使用缓存是可以控制的,如以下代码所示:

//使用缓存
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//不使用缓存  
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);

与JavaScript交互

作为示例的html文件内容如下:

<html>
  <head> 
    <meta charset="utf-8"> 
    <title>JSDemo</title> 
    <script>
       function alertMsg(msg) { 
         alert(msg) 
       }
       function toastMsg(msg) {
         window.control.showToast(msg)
       }
       function sumJava(num1, num2) {
         window.control.onSumResult(num1 + num2)
       }
       function sumJS(num1, num2) {
         return num1 + num2;
       } 
    </script>
  </head>
  <body>
    <button type="button" id="btn_toast" 
        onclick="toastMsg('show a Toast')">
      toast
    </button>
  </body>
</html>

在Java代码中调用JS方法

调用无返回值的JS方法

示例代码如下:

mWebView.post(new Runnable() { 
  @Override 
  public void run() { 
    mWebView.loadUrl("javascript:alertMessage(\"" + "Hello, World!" + "\")"); 
  }
});
调用有返回值的JS方法

在Android 4.4之前没有提供获取JS函数的返回值的方法,所以需要我们“曲线救国”,具体做法是在我们调用的有返回值的JS方法中调用Java方法,并把返回值通过Java方法传进来。如以下代码所示:

mWebView.post(new Runnable() { 
  @Override public void run() { 
    mWebView.loadUrl( "javascript:sumJava(1,2)"); 
  }
});

然后我们定义一个Java方法:

@JavascriptInterface
public void onSumResult(int result) {   
    //传入的result参数即为JS方法的返回值
}

以上代码中的@JavascriptInterface注解表示onSumResult方法会被JS代码调用,具体介绍请看下文。

从Android 4.4开始,我们可以直接使用evaluateJavascript方法来方便地获取JS方法的返回值,示例代码如下:

mWebView.evaluateJavascript("sumJS(1, 2)", new ValueCallback<String>() {
  @Override 
  public void onReceiveValue(String str) { 
    //str为JS方法的返回值的字符串形式
  } 
});

在JS代码中调用Java方法

方法一:通过window.control接口

首先在Java代码中定义如下方法:

@JavascriptInterface
public void showToast(String msg) {
  Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}

然后在JS代码中:

function toastMsg(msg) {
  window.control.showToast(msg)
}

通过这种方式调用Java方法很简单直接。

方法二:通过自定义接口

首先在Java代码中定义一个JSInterface类:

public class JSInterface { 
  Context mContext; 

  JSInterface(Context context) { 
    mContext = context; 
  } 

  @JavascriptInterface 
  public void showToast(String msg) { 
    Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); 
  }
}

然后还需要用addJavascriptInterface创建接口:

mWebView.addJavascriptInterface(new JSInterface(getApplicationContext()), 
    "toast");

这样一来我们在JS代码中就可以按如下方式调用showToast方法了:

function toastMsg(msg) {
  toast.showToast(msg)
}

关于addJavascriptInterface方法我们需要注意以下几点:

  • addJavascriptInterface方法中要绑定的Java对象运行在另外的线程中,而非构造它的线程中
  • 若targetSdkVersion大于等于17(android 4.2),则必须使用 @JavascriptInterface注解,否则JS代码会访问不到相应的Java方法

参考资料

  1. 谈谈WebView的使用
  2. 深入讲解WebView
  3. WebView你真的熟悉吗?看了才知道

长按或扫描二维码关注我们,让您利用每天等地铁的时间就能学会怎样写出优质app。


Android通俗说