WebView使用详解(三)——WebChromeClient

转自:http://blog.youkuaiyun.com/harvic880925/article/details/51583253

一、WebChromeClient

1、概述
(1)、 与WebViewClient的区别

很多同学一看到这里有Chrome,立马就会想到google 的Chrome浏览器;这里并不是指Chrome浏览器的意思,而是泛指浏览器,WebView的内部实现并不是完全使用Chrome的内核,而是部分使用Chome内核,其它都是与Chrome不相同的; 
我们再来对比下WebViewClient:

  • WebViewClient:在影响View的事件到来时,会通过WebViewClient中的方法回调通知用户
  • WebChromeClient:当影响浏览器的事件到来时,就会通过WebChromeClient中的方法回调通知用法。

通过上面的对比,我们发现WebViewClient和WebChromeClient都是针对不同事件的回调,而google将这些回调进行分类集合,就产生了WebViewClient、WebChromeClient这两个大类,其中管理着针对不同类型的回调而已。

(2)、为什么叫WebChromeClient?

因为WebChromeClient中集合了影响浏览器的事件到来时的回调方法,所以这里更需要突出浏览器的概念,而Chrome则是google自家的浏览器名称,所以使用WebChromeClient来做为名称吧,纯属臆想……

(3)、WebChromeClient的常用函数

我们先来看一下,在WebChromeClient中我们将要讲解的函数,其实WebChromeClient里的函数是非常多的,可以监控到浏览器的很多方面,这里我们就不再一个个来讲了,只讲解常用的几个函数,而且随着ReactNative和Hybird的普及,WebView的使用场景会越来越少,指不定哪一天就被废弃了

/**
 * 当网页调用alert()来弹出alert弹出框前回调,用以拦截alert()函数
 */
public boolean onJsAlert(WebView view, String url, String message,JsResult result)
/**
 * 当网页调用confirm()来弹出confirm弹出框前回调,用以拦截confirm()函数
 */
public boolean onJsConfirm(WebView view, String url, String message,JsResult result)
/**
 * 当网页调用prompt()来弹出prompt弹出框前回调,用以拦截prompt()函数
 */
 public boolean onJsPrompt(WebView view, String url, String message,String defaultValue, JsPromptResult result) 
 /**
 * 打印 console 信息
 */
 public boolean onConsoleMessage(ConsoleMessage consoleMessage)
 /**
 * 通知程序当前页面加载进度
 */
 public void onProgressChanged(WebView view, int newProgress)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
2、WebChromeClient之onJsAlert、onJsConfirm、onJsPrompt

这一小节我们把这三个函数一起看,因为他们都是为了处理弹出框。

(1)、为何JS中的alert()、confirm()、prompt()无效

首先,我们来举个例子,在网页中加上按钮,在点击时分别调用alert()、confirm()、prompt()来弹出不同的对话框 
web.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 id="h">欢迎光临启舰的blog</h1>
    <button onclick="confirm('傻叉')">confirm</button>
    <button onclick="alert('傻叉')">alert</button>
    <button onclick="prompt('傻叉')">prompt</button>
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

然后是Java处理代码:(MyActivity.java)

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

效果图如下: 
这里写图片描述 
从效果图中,虽然启用了JavaScript但是在网页中的confrim()、alert()、prompt()却是没有效果! 
这是因为我们需要设置WebChromClient! 
在程序中,我们只需要加上mWebView.setWebChromeClient(new WebChromeClient());就可以实现confrim()、alert()、prompt()的弹出效果了 
html代码不变,我们来看一下JAVA代码:

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient());

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

相比上面,只多了一句:mWebView.setWebChromeClient(new WebChromeClient()); 
现在再来看下效果图: 
这里写图片描述 
至于为什么会这样,我也不知道,也没仔细去研究它的源码,记住就好了。

(2)、使用onJsAlert拦截alert()函数概述

我们先来看一下在程序中如何来做的:

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
                result.confirm();
                return true;
            }
        });

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

效果图如下: 
我们在重写onJsAlert中,在onJsAlert代码中写了三句话:

Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
result.confirm();
return true;
  • 1
  • 2
  • 3

这三句话,句句非常重要: 
第一句:

Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
  • 1

表示我们拦截html中alert函数之后,我们自己的操作,这里是弹出一行toast 
第二句:

result.confirm();
  • 1

这句话非常重要,它表示向WebView通知操作结果,JsResult有两个函数:JsResult.confirm()和JsResult.cancel(),JsResult.confirm()表示点击了弹出框的确定按钮,JsResult.cancel()则表示点击了弹出框的取消按钮。 
如果没有使用JsResult来告诉WebView处理结果,则WebView就会认为这个弹出框还一直弹在那里,你再点击alert按钮,将会无效; 
第三句:

return true;
  • 1

表示告诉WebView我们已经拦截了alert()函数,不需要再弹出网页中的alert弹出框了,如果我们return false,那么WebView就会认为我们没有拦截alert()函数,会继续弹出alert对话框。

(3)、如果onJsAlert没有使用JsResult确认结果

如果我们把result.confirm()去掉,来看一下,是不是真的像我们说的那样再次点击alert按钮会失效

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
//                result.confirm();
                return true;
            }
        });

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

我们把上面代码中的 result.confirm()给注掉一下,看下效果: 
这里写图片描述 
从效果图中可以看到,在去掉 result.confirm()以后,只有第一次有效果,后面再点击alert按钮就无效了(动画中看不出来点击效果,其实我一直在点……)。

(4)、如果onJsAlert中return false会怎样

前面我们也讲到,如果在onJsAlert中return true,则表示告诉WebView我们已经拦截了alert函数,系统不需要再弹出alert对话框了,如果return false,则表示告诉WebView我们没有拦截alert函数,使用系统的默认处理,从WebChromeClient的源码中可以看到onJsAlert默认是return false的。

public boolean onJsAlert(WebView view, String url, String message,JsResult result) {
    return false;
}
  • 1
  • 2
  • 3

下面我们就来看看如何我们在弹出toast以后return false,系统是不是真的还会弹出alert对话框

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
                result.confirm();
                return false;
            }
        });

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

效果图如下: 
这里写图片描述 
从效果图中也明显看出了我们的结论是正确的,当return false时,不管我们前面做了什么,依然会弹出对话框。

好了,有关onJsAlert我们总结一下:

  • 如果需要使网页中的confrim()、alert()、prompt()函数生效,需要设置WebChromeClient!
  • 在使用onJsAlert来拦截alert对话框时,如果不需要再弹出alert对话框,一定要return true;在return false以后,会依然调用系统的默认处理来弹出对话框的
  • 如果我们return true,则需要在处理完成以后调用JsResult.confirm()或者JsResult.cancel()来告诉WebView我们点中哪个按钮来取消程序对话框。否则再次点击按钮将会失败

可能有些同学不知道confrim(),prompt()的对话框效果,下面就整体给大家演示一下html中原生的效果。 
这里写图片描述 
有关confrim()和prompt()的拦截,我们就不再讲了,与拦截alert()一样!

3、WebChromeClient之onConsoleMessage

当html中调用console相关输出的时候,就会通过onConsoleMessage进行通知

public boolean onConsoleMessage(ConsoleMessage consoleMessage)
  • 1

参数意义:

  • ConsoleMessage consoleMessage:保存着当前消息的类型和消息内容
  • 返回值:如果返回true时,就表示拦截了console输出,系统就不再通过console输出出来了,如果返回false则表示没有拦截console输出,调用系统默认处理。

我们来看下正常情况下,console输出的内容及用法 
先来看html内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 id="h">欢迎光临启舰的blog</h1>
    <button onclick="confirm('傻叉')">confirm</button>
    <button onclick="alert('傻叉')">alert</button>
    <button onclick="prompt('傻叉')">prompt</button>
    <button onclick="log()">log</button>
</body>
<script type="text/javascript">
function log(){
  console.log("console.log");
  console.warn("warnning");
  console.error("error");
}
</script>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

对应的java代码如下:

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

除了alert,prompt,confirm以外,其它时候都不需要强制设置WebChromeClient 
当点击log按钮时,会调用console的函数把log输出出来 
效果图如下: 
这里写图片描述 
在我们logcat中也可以看到如下日志: 
这里写图片描述

示例:拦截Console,弹出消息

下面我们就重写WebChromeClient的onConsoleMessage方法,把console消息拦截掉,然后把消息利用toast弹出来:

mWebView.setWebChromeClient(new WebChromeClient(){
    @Override
    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
        Toast.makeText(MyActivity.this,consoleMessage.message(),Toast.LENGTH_SHORT).show();
        return  true;
    }

});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

效果图如下: 
这里写图片描述 
由于我们return true,控制台将不会再看到我们的消息日志了。

4、WebChromeClient之onProgressChanged

表示当前页面的加载速度,函数声明如下:

public void onProgressChanged(WebView view, int newProgress)
  • 1
  • WebView view:当前WebView实例
  • int newProgress:当前的加载进度,值从0到100

比如我们加载“http://blog.youkuaiyun.com/harvic880925” 
然后把加载进度用日志打出来:

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                Log.e("qijian","progress:"+newProgress);
                super.onProgressChanged(view, newProgress);
            }
        });

        mWebView.loadUrl("http://blog.youkuaiyun.com/harvic880925");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

日志如下: 
这里写图片描述 
大家一定要注意,底层实现时,是利用handler来定时轮循当前进度的,每隔一定时间查询一次,所以每次拿到的进度数据是不一样的,也就是说如果页面较简单,可能会直接返回100,而跳过中间的各个数据。也就是说,除了100,其它任何一个数值不是一定返回的,所以大家如果要用到进度除了数值100可以用等号来判断,其它一定要用大于号或小于号,如果用了等号,可能永远也不会执行到!

5、WebChromeClient之其它函数

WebChromeClient除了上面几个常用函数以后,还有其它一些函数,下面就简单列举一下,就不再一一举例了

/*
* 通知页面标题变化
*/
nReceivedTitle(WebView view, String title)

/*
* 通知当前页面网站新图标
*/
onReceivedIcon(WebView view, Bitmap icon)

/*
* 通知主程序图标按钮URL
*/
onReceivedTouchIconUrl(WebView view, String url, boolean precomposed)

/*
* 通知主程序当前页面将要显示指定方向的View,该方法用来全屏播放视频。
*/
public interface CustomViewCallback {
       // 通知当前页面自定义的View被关闭
       public void onCustomViewHidden();
   }
onShowCustomView(View view, CustomViewCallback callback)

/*
* 与onShowCustomView对应,通知主程序当前页面将要关闭Custom View
*/
onHideCustomView()

/**
 * 请求主程序创建一个新的Window,如果主程序接收请求,返回true并创建一个新的WebView来装载Window,然后添加到View中,发送带有创建的WebView作为参数的resultMsg的给Target。如果主程序拒绝接收请求,则方法返回false。默认不做任何处理,返回false
 */
onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)

/*
* 显示当前WebView,为当前WebView获取焦点。
*/
onRequestFocus(WebView view)

/*
* 通知主程序关闭WebView,并从View中移除,WebCore停止任何的进行中的加载和JS功能。
*/
onCloseWindow(WebView window)

/**
 * 告诉客户端显示离开当前页面的导航提示框。如果返回true,由客户端处理确认提示框,调用合适的JsResult方法。如果返回false,则返回默认值true给javascript接受离开当前页面的导航。默认:false。JsResult设置false,当前页面取消导航提示,否则离开当前页面。
 */
onJsBeforeUnload(WebView view, String url, String message, JsResult result)

/**
 *通知主程序web内容尝试使用定位API,但是没有相关的权限。主程序需要调用调用指定的定位权限申请的回调。更多说明查看GeolocationPermissions相关API。
 */
onGeolocationPermissionsShowPrompt(String origin,GeolocationPermissions.Callback callback)

/*
 * 通知程序有定位权限请求。如果onGeolocationPermissionsShowPrompt权限申请操作被取消,则隐藏相关的UI界面。
 */
onGeolocationPermissionsHidePrompt()

/**
*通知主程序web内容尝试申请指定资源的权限(权限没有授权或已拒绝),主程序必须调用PermissionRequest#grant(String[])或PermissionRequest#deny()。如果没有覆写该方法,默认拒绝。
*/
onPermissionRequest(PermissionRequest request)

/**
* 通知主程序相关权限被取消。任何相关UI都应该隐藏掉。
*/
onPermissionRequestCanceled(PermissionRequest request)

/**
* 通知主程序 执行的Js操作超时。客户端决定是否中断JavaScript继续执行。如果客户端返回true,JavaScript中断执行。如果客户端返回false,则执行继续。注意:如果继续执行,重置JavaScript超时计时器。如果Js下一次检查点仍没有结束,则再次提示。
*/
onJsTimeout()

/**
*当停止播放,Video显示为一张图片。默认图片可以通过HTML的Video的poster属性标签来指定。如果poster属性不存在,则使用默认的poster。该方法允许ChromeClient提供默认图片。
*/
getDefaultVideoPoster()

/**
* 当用户重放视频,在渲染第一帧前需要花费时间去缓冲足够的数据。在缓冲期间,ChromeClient可以提供一个显示的View。如:可以显示一个加载动画。
*/
getVideoLoadingProgressView()

/**
* 获取访问历史Item,用于链接颜色。
*/
getVisitedHistory(ValueCallback callback)

/**
* 通知客户端显示文件选择器。用来处理file类型的HTML标签,响应用户点击选择文件的按钮操作。调用filePathCallback.onReceiveValue(null)并返回true取消请求操作。
* FileChooserParams参数的枚举列表:
MODE_OPEN 打开
MODE_OPEN_MULTIPLE 选中多个文件打开
MODE_OPEN_FOLDER 打开文件夹(暂不支持)
MODE_SAVE 保存
*/
onShowFileChooser(WebView webView, ValueCallback filePathCallback,FileChooserParams fileChooserParams)

/**
* 解析文件选择Activity返回的结果。需要和createIntent一起使用。
*/
parseResult(int resultCode, Intent data)

/**
* 创建Intent对象来启动文件选择器。Intent支持可访问的简单类型文件资源。不支持高级文件资源如live media capture媒体快照。如果需要访问这些资源或其他高级文件类型资源可以自己创建Intent对象。
*/
createIntent()

/**
* 返回文件选择模式
*/
getMode()

/**
* 返回可访问MIME类型数组,如audio/*,如果没有指定可访问类型,数组返回为null
*/
getAcceptTypes()

/**
* 返回优先的媒体快照类型值如Camera、Microphone。true:允许快照。false,禁止快照。使用getAcceptTypes方法确定合适的capture设备。
*/
isCaptureEnabled()

/**
* 返回文件选择器的标题。如果为null,使用默认名称。
*/
getTitle()

/**
*指定默认选中的文件名或为null
*/
getFilenameHint()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值