需求:App 啟動直接載入指定網址 (如 https://www.baidu.com),無需使用者手動輸入,並提供載入進度條、JS 支援、雙擊縮放、錯誤頁面友善提示。本文提供生產級 WebView 封裝代碼,涵蓋 WebViewClient、WebChromeClient、生命週期管理、權限配置全細節。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:usesCleartextTraffic="true" <!-- HTTP 明文流量 (API 28+) -->
...>
<activity android:name=".MainActivity">...</activity>
</application>
<FrameLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 載入指示器 -->
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:max="100"
android:progress="0"
android:visibility="gone" />
<TextView
android:id="@+id/loading_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="載入中..."
android:visibility="gone" />
</FrameLayout>
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
private lateinit var progressBar: ProgressBar
private lateinit var loadingText: TextView
private val targetUrl = "https://www.baidu.com" // 可從 Intent/設定讀取
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = findViewById(R.id.webview)
progressBar = findViewById(R.id.progress_bar)
loadingText = findViewById(R.id.loading_text)
initWebView()
loadUrl(targetUrl)
}
private fun initWebView() {
val settings = webView.settings.apply {
javaScriptEnabled = true // 1. 啟用 JS
domStorageEnabled = true // 2. LocalStorage
databaseEnabled = true
useWideViewPort = true // 3. 支援 viewport
loadWithOverviewMode = true
builtInZoomControls = true // 4. 內建縮放控制
displayZoomControls = false // 隱藏縮放按鈕
supportZoom = true
mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW // HTTP/HTTPS 混合
cacheMode = WebSettings.LOAD_DEFAULT
}
// 5. 客製化 WebViewClient:攔截連結、錯誤處理
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
view.loadUrl(request.url.toString())
return true
}
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
showErrorPage(error.description.toString())
}
override fun onPageFinished(view: WebView, url: String) {
loadingText.visibility = View.GONE
progressBar.visibility = View.GONE
}
}
// 6. 客製化 WebChromeClient:進度條
webView.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView, newProgress: Int) {
progressBar.progress = newProgress
if (newProgress < 100) {
progressBar.visibility = View.VISIBLE
loadingText.visibility = View.VISIBLE
} else {
progressBar.visibility = View.GONE
loadingText.visibility = View.GONE
}
}
}
}
private fun loadUrl(url: String) {
webView.loadUrl(url)
}
private fun showErrorPage(msg: String) {
webView.loadDataWithBaseURL(null,
"<html><body><h1>載入失敗</h1><p>$msg</p></body></html>",
"text/html", "UTF-8", null)
}
// 7. 生命週期:釋放資源
override fun onDestroy() {
webView.loadUrl("about:blank")
webView.stopLoading()
webView.webViewClient = null
webView.webChromeClient = null
webView.destroy()
super.onDestroy()
}
// 8. 返回鍵處理:優先網頁後退
override fun onBackPressed() {
if (webView.canGoBack()) webView.goBack()
else super.onBackPressed()
}
}
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setBuiltInZoomControls(true);
settings.setDisplayZoomControls(false);
settings.setSupportZoom(true);
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
settings.setDomStorageEnabled(true);
| 問題 | 原因 | 解決 |
|---|---|---|
| 網頁空白/白屏 | 未開啟 JS、HTTP 明文被阻擋 | javaScriptEnabled=true + usesCleartextTraffic=true |
| 點擊連結跳出系統瀏覽器 | 未覆寫 shouldOverrideUrlLoading | 回傳 true 並 view.loadUrl(url) |
| 進度條不消失 | 未在 onPageFinished 隱藏 | 正確實作 WebChromeClient.onProgressChanged |
| 記憶體洩漏 | Activity 銷毀未清理 WebView | onDestroy 中完整釋放 |
// Cookie 同步 (登入態共享)
CookieManager.getInstance().setAcceptCookie(true);
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
// 自訂 UserAgent
settings.setUserAgentString(settings.getUserAgentString() + " MyApp/1.0");
一個生產可用的 WebView 封裝需涵蓋:權限配置、JS/儲存/縮放/混合內容啟用、WebViewClient 處理導航與錯誤、WebChromeClient 進度回報、生命週期釋放、返回鍵攔截。上述 Kotlin/Java 完整代碼可直接整合至專案,並作為進一步客製化 (如 JSBridge、離線快取、影音播放) 的基礎骨架。