引言:为什么你的App需要这个“万能口袋”?
想象一下这个场景:你正在刷一个购物App,看到一篇超棒的穿搭攻略,你一点击,App居然“嗖”地一下跳转到了手机自带的浏览器!这种感觉,就像你正吃着火锅唱着歌,突然被麻匪劫了道,体验感瞬间清零。
这时候,WebView就该闪亮登场了!它不是什么黑科技,你可以把它理解成 “镶嵌在你自己App里的一个迷你浏览器内核” 。有了它,用户不用离开你的App,就能无缝浏览网页内容。无论是展示用户协议、新闻资讯,还是内嵌一个H5小游戏,WebView都是你的不二之选。
今天,咱不整那些虚头巴脑的概念,就跟我一起,手把手把这个“万能口袋”打造得既实用又酷炫。系好安全带,老司机要发车了!
第一章:WebView初体验——从“Hello, World”级别开始
万事开头难?不,开头很简单。
1.1 权限,咱得先“上牌”
想在App里畅游网络世界,首先得在AndroidManifest.xml文件里拿到“上网许可证”:
<uses-permission android:name="android.permission.INTERNET" />
这条权限就像是给你的App办了张“宽带套餐”,没它?WebView就只能在家玩单机游戏了。
1.2 布局里,给它个“家”
在布局文件activity_main.xml里,给WebView划块地儿:
<WebView
android:id="@+id/myWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
简单粗暴,全屏都是它的地盘。
1.3 代码里,让它“动起来”
在MainActivity中,几行代码就能让它显示网页:
class MainActivity : AppCompatActivity() {
private lateinit var myWebView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myWebView = findViewById(R.id.myWebView)
// 基本设置
val webSettings = myWebView.settings
webSettings.javaScriptEnabled = true // 让WebView能执行JS,很重要!
// 加载网页
myWebView.loadUrl("https://www.baidu.com")
}
}
搞定!运行一下,你的App里已经能显示百度了。是不是简单得有点不敢相信?但先别高兴太早,这只是一个“毛坯房”,用户体验堪比“战地记者”——只能看,不能互动,遇到问题就卡死。
第二章:高级装修——把“毛坯房”升级成“智能精装”
一个合格的浏览器,怎么能没有前进、后退、刷新?怎么能任由网页跳转到系统浏览器?下面,我们就开始精装修。
2.1 拦截“不速之客”——自定义WebViewClient
默认情况下,你点击网页里的链接,它会问系统:“嘿,哪个App能处理这个链接?”然后可能就打开了Chrome。这不行!我们必须拦截它,告诉它:“在我这一亩三分地,就得听我的!”
这就需要请出我们的“门神”——WebViewClient。
myWebView.webViewClient = object : WebViewClient() {
// 这个方法是关键!当有新的链接需要加载时,它会问我们:“老板,这个货咱自己吞了吗?”
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
val url = request?.url.toString()
// 如果这个URL不是我们信任的基域,可以选择用外部浏览器打开
// 但这里,我们选择所有链接都在WebView内部打开
view?.loadUrl(url)
return true // 返回true表示:我处理了,你别管了。
}
// 网页开始加载
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
// 这里可以显示一个加载中的进度条
showLoadingProgress()
}
// 网页加载结束
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
// 隐藏加载进度条
hideLoadingProgress()
// 更新一下工具栏的标题,显示网页自己的标题
supportActionBar?.title = view?.title
}
// 遇到错误(比如没网)
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
super.onReceivedError(view, request, error)
// 可以显示一个友好的错误页面,比如一个“网络开小差了”的提示和重试按钮
showErrorPage()
}
}
看,有了WebViewClient,我们就完全掌控了网页的加载过程,用户体验直接提升一个档次。
2.2 记录“来时路”——实现前进后退
用户逛网页,就像在逛超市,得有购物车(历史记录)才能前进后退。WebView自带了这个“购物车”,我们只需要提供几个按钮来操作它。
首先,在布局里加个简单的底部工具栏:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#f0f0f0">
<Button
android:id="@+id/btnBack"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="后退" />
<Button
android:id="@+id/btnForward"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="前进" />
<Button
android:id="@+id/btnReload"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="刷新" />
</LinearLayout>
然后在Activity里给这些按钮赋予灵魂:
private fun setupNavigation() {
findViewById<Button>(R.id.btnBack).setOnClickListener {
if (myWebView.canGoBack()) {
myWebView.goBack() // 回到上一页
} else {
// 如果不能后退了,可能是退出App,或者给个提示
finish()
}
}
findViewById<Button>(R.id.btnForward).setOnClickListener {
if (myWebView.canGoForward()) {
myWebView.goForward() // 前进到下一页
}
}
findViewById<Button>(R.id.btnReload).setOnClickListener {
myWebView.reload() // 刷新
}
}
2.3 给加载加个“进度条”——让等待不再焦虑
网页加载像等外卖,有个进度条心里就踏实多了。WebView提供了一个好伙伴WebChromeClient来帮我们获取加载进度。
myWebView.webChromeClient = object : WebChromeClient() {
// 当网页加载进度发生变化时,这个方法会被调用
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
// newProgress 是一个0-100的整数
if (newProgress < 100) {
// 更新你的进度条UI,比如一个横向的ProgressBar
updateProgressBar(newProgress)
} else {
// 加载完成,隐藏进度条
hideProgressBar()
}
}
}
第三章:应对“江湖险恶”——处理各种疑难杂症
现实世界是残酷的,你的WebView会遇到各种“坑”。
3.1 权限申请——当网页想“用你的麦克风”
如果网页里要使用摄像头、麦克风、定位等,你需要重写WebChromeClient的相关方法,并自己在Activity中处理权限申请。这是个细活儿,但套路固定:
myWebView.webChromeClient = object : WebChromeClient() {
// 处理网页请求权限(如摄像头、麦克风)
override fun onPermissionRequest(request: PermissionRequest?) {
// 1. 先检查我们自己有没有这个权限(比如RECORD_AUDIO, CAMERA)
// 2. 如果没有,向用户申请
// 3. 用户授权后,调用 request.grant(request.resources) 把权限“转交”给网页
// 4. 用户拒绝,调用 request.deny()
// 注意:这里涉及到Android的动态权限申请,代码略长,但逻辑清晰
}
}
3.2 与JavaScript“勾肩搭背”——原生与网页的通信
这是WebView最强大的功能之一!你可以让网页里的JavaScript调用你App里的原生方法。
比如,网页里有个按钮,一点击,就能在你的App里弹出一个原生Toast。
首先,在Kotlin端,创建一个充当“桥梁”的对象:
class WebAppInterface(private val context: Context) {
// 这个方法将被JavaScript调用
@JavascriptInterface
fun showToast(toast: String) {
Toast.makeText(context, toast, Toast.LENGTH_SHORT).show()
}
}
然后,把这个桥梁对象“安装”到WebView上:
myWebView.addJavascriptInterface(WebAppInterface(this), "Android")
// "Android" 是给JavaScript端调用的对象名
最后,在网页的JavaScript里,就可以这么玩了:
// 当按钮被点击时
function onButtonClick() {
// 调用原生Android方法
Android.showToast("Hello from Web!");
}
这样一来,你的App和H5页面就成了亲密无间的战友,可以实现无比灵活的功能。
第四章:完整代码示例——“开箱即用”的终极武器
理论说了这么多,是时候上硬菜了!下面是一个整合了以上所有功能的、相对完整的Activity代码。
(注意:为了简洁,此处代码省略了部分细节,如动态权限申请的完整代码,但核心逻辑俱全)
class MainActivity : AppCompatActivity() {
private lateinit var myWebView: WebView
private lateinit var progressBar: ProgressBar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initViews()
setupWebView()
setupNavigation()
// 加载初始页面
myWebView.loadUrl("https://www.baidu.com")
}
private fun initViews() {
myWebView = findViewById(R.id.myWebView)
progressBar = findViewById(R.id.progressBar) // 假设布局里有个横向ProgressBar
}
private fun setupWebView() {
val webSettings = myWebView.settings
webSettings.javaScriptEnabled = true
webSettings.domStorageEnabled = true // 开启DOM存储,对复杂H5应用很重要
webSettings.loadWithOverviewMode = true
webSettings.useWideViewPort = true
// 设置WebViewClient
myWebView.webViewClient = MyWebViewClient()
// 设置WebChromeClient
myWebView.webChromeClient = MyChromeClient()
// 添加JS接口
myWebView.addJavascriptInterface(WebAppInterface(this), "Android")
}
private fun setupNavigation() {
// ... 同上文,设置后退、前进、刷新按钮的点击事件
}
// 处理返回键
override fun onBackPressed() {
if (myWebView.canGoBack()) {
myWebView.goBack()
} else {
super.onBackPressed()
}
}
// 自定义WebViewClient
inner class MyWebViewClient : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
view.loadUrl(request.url.toString())
return true
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
progressBar.visibility = View.VISIBLE
}
override fun onPageFinished(view: WebView?, url: String?) {
progressBar.visibility = View.GONE
}
}
// 自定义WebChromeClient
inner class MyChromeClient : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
progressBar.progress = newProgress
}
}
}
// JS桥梁类
class WebAppInterface(private val context: Context) {
@JavascriptInterface
fun showToast(message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}
结语:你的“万能口袋”,远不止于此
恭喜你!走到这一步,你已经不是一个WebView小白了。你亲手打造了一个不仅能用,而且好用的内嵌浏览器。它具备了:
- 内部加载:不让用户“跳戏”。
- 导航功能:前进后退刷新,随心所欲。
- 视觉反馈:进度条让等待不再枯燥。
- 深度交互:能与JavaScript互通有无。
但这仅仅是开始。WebView的潜力远不止于此,你还可以探索:
- 文件上传:处理
<input type="file">。 - 下载管理:拦截下载链接,用系统的DownloadManager处理。
- 自定义错误页面:设计得比原生提示好看一百倍。
- 缓存策略:让二次加载快如闪电。
希望这篇“有网感”的深度教程,能真正帮你把WebView这个“万能口袋”玩出花来。代码就在这里,大胆地复制、粘贴、修改、折腾吧!遇到问题就去搜,就去问,这才是程序员成长的正确姿势。
祝你编码愉快,我们下次再见!
441

被折叠的 条评论
为什么被折叠?



