最近接到了个需求,需要让原生的WebView实现打开新标签页的功能。
WebView想要实现打开新标签页,其实就是在WebChromeClient
的onCreateWindow
回调方法中创建一个新的WebView来加载新的网页,下面介绍下如何实现。
WebView支持打开新标签页
1. 修改WebSettings
修改WebSettings,开启支持多窗口。
注意:H5使用window.open()和window.close()来实现开关新标签页,需要开启javaScript。H5使用href+target实现打开新标签页,可以不开启javaScript。
代码如下:
val settings=webView.settings
//javaScript非必须开启
settings.javaScriptEnabled = true
settings.javaScriptCanOpenWindowsAutomatically = true
//开启支持多窗口
settings.setSupportMultipleWindows(true)
复制代码
2. 设置WebChromeClient
创建WebChromeClient
并重写onCreateWindow
和onCloseWindow
方法,代码如下:
webView.webChromeClient = object : WebChromeClient() {
override fun onCreateWindow(view: WebView?, isDialog: Boolean, isUserGesture: Boolean, resultMsg: Message?): Boolean {
//创建新WebView,并使用新WebView来加载新标签页的链接
resultMsg?.run {
val webViewTransport = obj as? WebView.WebViewTransport
webViewTransport?.let { transport ->
transport.webView = WebView(context)
}
sendToTarget()
}
return true
}
override fun onCloseWindow(window: WebView?) {
super.onCloseWindow(window)
//销毁新建的WebView
}
}
复制代码
3. 完整代码
做了个示例,完整代码如下:
- 初始页H5
<!DOCTYPE html>
<html lang=zh-CN>
<head>
<meta charset=utf-8>
<title>test</title>
<script>
function openNewWindow(){
window.open("file:///android_asset/index_new_tab.html")
}
function androidCallJsWithParams(arg){
document.getElementById("message").innerHTML += (arg);
}
</script>
</head>
<body>
<div style="position:relative;left:40px;top:100px">
<p id='message' style="font-size:24px;position:relative;top:20px">receive:</p>
<a href="file:///android_asset/index_new_tab.html" target="_blank" style="font-size:24px;position:relative;top:20px">use a tag</a>
<button type="button" style="width:280px;height:88px;font-size:24px;position:relative;left:20px;top:30px"
onclick="openNewWindow()">
use window open
</button>
</div>
</body>
</html>
复制代码
- 新标签页H5
<!DOCTYPE html>
<html lang=zh-CN>
<head>
<meta charset=utf-8>
<title>test</title>
<script>
function closeNewWindow(){
window.close()
}
function androidCallJsWithParams(arg){
document.getElementById("message").innerHTML += (arg);
}
</script>
</head>
<body>
<div style="position:relative;left:40px;top:100px">
<p id='message' style="font-size:24px;position:relative;top:20px">receive:</p>
<button type="button" style="width:280px;height:88px;font-size:24px;position:relative;top:30px"
onclick="closeNewWindow()">
use window close
</button>
</div>
</body>
</html>
复制代码
- WebViewActivity以及布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/web_view_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/pb_web_load_progress"
android:layout_width="0dp"
android:layout_height="4dp"
android:max="100"
android:progress="50"
android:progressDrawable="@drawable/layer_progress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_back"
android:layout_width="52dp"
android:layout_height="25dp"
android:layout_marginStart="18dp"
android:layout_marginTop="21dp"
android:src="@mipmap/icon_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
class WebViewActivity : Activity() {
private lateinit var layoutWebViewActivityBinding: LayoutWebViewActivityBinding
private var mainWebView: WebView? = null
private var newWebView: WebView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
layoutWebViewActivityBinding = DataBindingUtil.setContentView(this, R.layout.layout_web_view_activity)
layoutWebViewActivityBinding.ivBack.setOnClickListener { onBackPressed() }
mainWebView = WebView(this)
mainWebView?.let {
it.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
initWebViewSetting(it)
layoutWebViewActivityBinding.webViewContainer.addView(it)
}
mainWebView?.loadUrl("file:///android_asset/index_open_tab.html")
}
@SuppressLint("JavascriptInterface", "SetJavaScriptEnabled")
private fun initWebViewSetting(webView: WebView?) {
webView?.run {
settings.cacheMode = WebSettings.LOAD_DEFAULT
settings.domStorageEnabled = true
settings.allowContentAccess = true
settings.allowFileAccess = true
settings.useWideViewPort = true
settings.loadWithOverviewMode = true
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
settings.javaScriptEnabled = true
settings.javaScriptCanOpenWindowsAutomatically = true
//开启支持多窗口
settings.setSupportMultipleWindows(true)
webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView, newProgress: Int) {
super.onProgressChanged(view, newProgress)
layoutWebViewActivityBinding.pbWebLoadProgress.run {
post { progress = newProgress }
if (newProgress >= 100 && visibility == View.VISIBLE) {
postDelayed({ visibility = View.GONE }, 500)
}
}
}
override fun onCreateWindow(view: WebView?, isDialog: Boolean, isUserGesture: Boolean, resultMsg: Message?): Boolean {
newWebView = WebView(this@WebViewActivity)
newWebView?.let { newWeb ->
newWeb.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
initWebViewSetting(newWeb)
layoutWebViewActivityBinding.webViewContainer.addView(newWeb)
resultMsg?.run {
val webViewTransport = obj as? WebView.WebViewTransport
webViewTransport?.let { transport ->
transport.webView = newWeb
}
sendToTarget()
}
}
return true
}
override fun onCloseWindow(window: WebView?) {
//销毁新建的WebView
destroyNewWebView()
super.onCloseWindow(window)
}
}
webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
layoutWebViewActivityBinding.pbWebLoadProgress.run {
post { visibility = View.VISIBLE }
}
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
//将WebView信息传到H5页面中
val message="mainWeb:$mainWebView||newWeb:$newWebView||currentWeb:$view"
view?.loadUrl("javascript:androidCallJsWithParams(\"$message\")")
}
}
}
}
override fun onDestroy() {
destroyMainWebView()
destroyNewWebView()
super.onDestroy()
}
override fun onBackPressed() {
when {
newWebView?.canGoBack() == true -> newWebView?.goBack()
newWebView != null -> destroyNewWebView()
mainWebView?.canGoBack() == true -> mainWebView?.goBack()
else -> super.onBackPressed()
}
}
private fun destroyMainWebView() {
mainWebView?.run {
Log.i(TAG, "destroy mainWeb webView:$this")
clearHistory()
loadDataWithBaseURL(null, "", "text/html", "utf-8", null)
layoutWebViewActivityBinding.webViewContainer.removeView(this)
destroy()
}
mainWebView = null
}
private fun destroyNewWebView() {
newWebView?.run {
Log.i(TAG, "destroy newWeb webView:$this")
clearHistory()
loadDataWithBaseURL(null, "", "text/html", "utf-8", null)
layoutWebViewActivityBinding.webViewContainer.removeView(this)
destroy()
}
newWebView = null
}
}
复制代码
实现效果如图: