Android(安卓)WebView开发全攻略
WebView是一个基于WebKit引擎、展现Web页面的控件,Android的WebView在低版本和高版本采用了不同的WebKit版本内核
基本使用
WebView的最简单的使用方式即是直接显示网页内容,首先别忘了添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
<WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent" />
webview.loadUrl("http://www.baidu.com") webview.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { //在本WebView中直接显示网页 view.loadUrl(url) return true } }
WebView常用方法
状态
webview.onResume() //激活WebView为活跃状态,能正常执行网页的响应 webview.onPause() //当页面被失去焦点被切换到后台不可见状态,执行webview.pauseTimers() //当应用程序(存在webview)被切换到后台时,这个方法针对的是全局的webview,它会暂停所有webview的布局显示、解析、延时,从而降低CPU功耗webview.resumeTimers() //恢复pauseTimers状态//销毁webview,先从父容器移除,再销毁father.removeView(webview)webview.destroy()
前进或后退网页
//是否可以后退webview.canGoBack()//后退网页webview.goBack()//是否可以前进webview.canGoForward()//前进网页webview.goForward()//以当前的index为起始点前进或者后退到历史记录中指定的steps//如果steps为负数则为后退,正数则为前进webview.goBackOrForward(steps)
常见用法:在Activity中处理该Back键事件
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if ((keyCode == KEYCODE_BACK) && webview.canGoBack()) { webview.goBack() return true } return super.onKeyDown(keyCode, event) }
清除缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.webview.clearCache(true)//清除当前webview访问的历史记录webview.clearHistory()//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据webview.clearFormData()
WebSettings类
作用:对WebView进行配置和管理
下面是一些常规设置
val webSettings: WebSettings = webview.settings if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { //解决网页中图片显示不出问题 webSettings.mixedContentMode = WebSettings.LOAD_DEFAULT } webSettings.javaScriptEnabled = true //支持JS webSettings.domStorageEnabled = true //支持DOM Storage webSettings.defaultTextEncodingName = "utf-8" //设置编码格式 webSettings.pluginState = WebSettings.PluginState.ON// 支持插件 webSettings.loadsImagesAutomatically = true //支持自动加载图片 webSettings.setSupportZoom(true) //支持缩放,默认为true。是下面那个的前提。 webSettings.builtInZoomControls = true //设置内置的缩放控件。若为false,则该WebView不可缩放 webSettings.displayZoomControls = false //隐藏原生的缩放控件 webSettings.databaseEnabled = true // 数据库存储API是否可用 webSettings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN //WebView底层布局算法 webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK //设置缓存,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。本地没有缓存时才从网络上获取 webSettings.allowFileAccess = true //设置可以访问文件 webSettings.javaScriptCanOpenWindowsAutomatically = true //支持通过JS打开新窗口 //设置自适应屏幕,两者合用 webSettings.useWideViewPort = true //将图片调整到适合WebView的大小 webSettings.loadWithOverviewMode = true // 缩放至屏幕的大小
WebViewClient类
作用:处理各种通知、请求事件
(1)shouldOverrideUrlLoading()
作用:打开网页时,不调用系统浏览器进行打开,而是在本WebView中直接显示
(2)onPageStarted()
作用:开始载入页面时调用此方法,在这里我们可以设定一个loading的页面,告诉用户程序正在等待网络响应
(3)onPageFinished()
作用:在页面加载结束时调用,我们可以关闭loading 条,切换程序动作
(4)onLoadResource()
作用:在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次
(5)onReceivedSslError()
作用:处理https请求,WebView默认是不处理https请求的,页面显示空白
webview.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { //使用WebView加载显示url view.loadUrl(url) return true } override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { //设定加载开始的操作 } override fun onPageFinished(view: WebView?, url: String?) { //设定加载结束的操作 } override fun onLoadResource(view: WebView?, url: String?) { //设定加载资源的操作 } override fun onReceivedError( view: WebView?, errorCode: Int, description: String?, failingUrl: String? ) { when (errorCode) { HttpStatus.NOT_FOUND -> view!!.loadUrl("file:///android_assets/error_handle.html") } } override fun onReceivedSslError( view: WebView?, handler: SslErrorHandler?, error: SslError? ) { // 接受所有网站的证书,忽略SSL错误,执行访问网页 handler?.proceed()// handler?.cancel() //表示挂起连接,为默认方式// handler?.handleMessage(msg!!) //可做其他处理 } }
WebChromeClient类
作用:辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等
(1)onProgressChanged()
作用:获得网页的加载进度并显示
(2)onReceivedTitle()
作用:获取Web页中的标题
webview.webChromeClient = object : WebChromeClient() { override fun onProgressChanged(view: WebView?, newProgress: Int) { if (newProgress < 100) progressbar.text = "${newProgress}%" else progressbar.text = "100%" } override fun onReceivedTitle(view: WebView?, title: String?) { title_text.text = title } }
WebView与JS的交互
Android通过WebView调用 JS 代码
注意:JS代码调用一定要在 onPageFinished() 回调之后才能调用,否则不会调用
(1)通过WebView的loadUrl()
// 先载入JS代码 webview.loadUrl(url) // 调用javascript的callJS()方法,注意调用的JS方法名要对应上 webview.loadUrl("javascript:callJS()")
(2)通过WebView的evaluateJavascript()
该方法比第一种方法效率更高、使用更简洁。因为该方法的执行不会使页面刷新,而第一种方法(loadUrl)会,该方法在Android 4.4 后才可使用,不过现在的安卓版本都在4.4以上了,所以这点不用在意了。如果需要返回值的话,请务必使用这个方法
webview.evaluateJavascript("javascript:callJS()", object : ValueCallback<String> { override fun onReceiveValue(p0: String?) { //此处为 js 返回的结果 } })
JS通过WebView调用 Android 代码
(1)通过WebView的addJavascriptInterface()进行对象映射
//定义一个内部类 inner class AndroidJSInterFace{ // 定义JS需要调用的方法,被JS调用的方法必须加入@JavascriptInterface注解 @JavascriptInterface fun hello(msg: String?) { runOnUiThread{ title_text.text=msg } } }
//AndroidJSInterFace类对象映射到js的test对象 webview.addJavascriptInterface(AndroidJSInterFace(), "test")
(2)在Android通过WebViewClient复写shouldOverrideUrlLoading()
webview.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { // 根据协议的参数,判断是否是所需要的url // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数) //假如约定好的传入进来的url = "js://webview?arg1=111&arg2=222" val uri = Uri.parse(url) if (uri.scheme == "js" && uri.authority == "webview") { // 执行JS所需要调用的逻辑 Log.d("TestLog", "js调用了Android的方法") // 也可以在协议上带有参数并传递到Android上 val collection = uri.queryParameterNames } return true } }
如果JS想要得到Android方法的返回值,只能通过 WebView 的 loadUrl ()去执行 JS 方法把返回值传递回去,相关的代码如下:
webview.loadUrl("javascript:returnResult($result)")
(3)通过 WebChromeClient 的onJsAlert()、onJsConfirm() 、onJsPrompt() 方法回调拦截JS对话框alert()、confirm()、prompt()消息
常用的拦截是:拦截 JS的prompt()方法
因为只有prompt()可以返回任意类型的值,操作最全面方便、更加灵活,而alert()没有返回值,confirm()只能返回两种状态(确定 / 取消)两个值
webview.webChromeClient = object : WebChromeClient() { override fun onJsPrompt( view: WebView?, url: String?, message: String?, defaultValue: String?, result: JsPromptResult? ): Boolean { //输入框 // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数) //假定传入进来的需要拦截的 url = "js://webview?arg1=111&arg2=222" val uri = Uri.parse(message) if (uri.scheme == "js" && uri.authority == "webview") { // 执行JS所需要调用的逻辑 Log.d("WebviewTest", "js调用了Android的方法") // 可以在协议上带有参数并传递到Android上 val collection = uri.queryParameterNames //参数result:代表消息框的返回值(输入值) result!!.confirm("js调用了Android的方法成功啦") return true } return super.onJsPrompt(view, url, message, defaultValue, result) } override fun onJsAlert( view: WebView?, url: String?, message: String?, result: JsResult? ): Boolean { //警告框 return super.onJsAlert(view, url, message, result) } override fun onJsConfirm( view: WebView?, url: String?, message: String?, result: JsResult? ): Boolean { //确认框 return super.onJsConfirm(view, url, message, result) } }
WebView调起系统相机和相册
(1)初始化需要的变量
private var uploadMessageAboveL: ValueCallback<Array<Uri>>? = null private var cameraFielPath: String? = null private var uploadMessage: ValueCallback<Uri>? = null
webview.webChromeClient = object :WebChromeClient(){ override fun onShowFileChooser( webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams? ): Boolean { uploadMessageAboveL = filePathCallback openImageChooserActivity() return true } // For Android < 3.0 fun openFileChooser(valueCallback: ValueCallback<Uri>) { uploadMessage = valueCallback openImageChooserActivity() } }
// 2.回调方法触发本地选择文件 private fun openImageChooserActivity() { //拍照 val imageStorageDir = File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "heart_image" ) if (!imageStorageDir.exists()) { imageStorageDir.mkdirs() } cameraFielPath = imageStorageDir.toString() + File.separator + "IMG_" + System.currentTimeMillis() .toString() + ".jpg" val file = File(cameraFielPath) //需要显示应用的意图列表,这个list的顺序和选择菜单上的图标顺序是相关的,请注意。 val cameraIntents: MutableList<Intent> = ArrayList() val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) val packageManager: PackageManager = packageManager //获取手机里所有注册相机接收意图的应用程序,放到意图列表里(无他相机,美颜相机等第三方相机) val listCam: List<ResolveInfo> = packageManager.queryIntentActivities(captureIntent, 0) for (res in listCam) { val packageName: String = res.activityInfo.packageName val i = Intent(captureIntent) i.component = ComponentName(res.activityInfo.packageName, res.activityInfo.name) i.setPackage(packageName) i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)) cameraIntents.add(i) } //相册 val i = Intent(Intent.ACTION_GET_CONTENT) i.action = Intent.ACTION_PICK i.type = "image/*" val chooserIntent = Intent.createChooser(i, "选择") chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toTypedArray()) startActivityForResult( chooserIntent, 101 ) }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == 101) { val result = if (data == null || resultCode != Activity.RESULT_OK) null else data.data if (uploadMessageAboveL != null) { onActivityResultAboveL(requestCode, resultCode, data) } else if (uploadMessage != null) { uploadMessage?.onReceiveValue(result) uploadMessage = null } if (resultCode != RESULT_OK) { //这里uploadMessage跟uploadMessageAboveL在不同系统版本下分别持有了 //WebView对象,在用户取消文件选择器的情况下,需给onReceiveValue传null返回值 //否则WebView在未收到返回值的情况下,无法进行任何操作,文件选择器会失效 if (uploadMessage != null) { uploadMessage?.onReceiveValue(null) uploadMessage = null } else if (uploadMessageAboveL != null) { uploadMessageAboveL?.onReceiveValue(null) uploadMessageAboveL = null } } } }
// 4. 选择内容回调到Html页面 @TargetApi(Build.VERSION_CODES.LOLLIPOP) fun onActivityResultAboveL(requestCode: Int, resultCode: Int, intent: Intent?) { if (requestCode != 101 || uploadMessageAboveL == null) return var results = arrayOf<Uri>() var result: Uri? = null if (resultCode == Activity.RESULT_OK) { if (intent != null) { var dataString = intent.dataString var clipData = intent.clipData if (clipData != null) { for (i in 0 until clipData.itemCount) { var itemAt = clipData.getItemAt(i) results[i] = itemAt.uri } } if (dataString != null) { results = arrayOf<Uri>(Uri.parse(dataString)) } uploadMessageAboveL?.onReceiveValue(results) uploadMessageAboveL = null } else { // if (result == null && File(cameraFielPath).exists()) { result = Uri.fromFile(File(cameraFielPath)) } uploadMessageAboveL?.onReceiveValue(arrayOf(result!!)) uploadMessageAboveL = null } } }
WebView响应下载点击事件
webview.setDownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> val uri = Uri.parse(url) val intent = Intent(Intent.ACTION_VIEW, uri) startActivity(intent) }
更多相关文章
- Activity类的runOnUiThread方法
- 我的javaSE学习笔记
- android之数据库和Content Provider(二)
- android 面试经典(4)
- Android进程间通讯——AIDL
- android Camera api1和api2学习笔记
- Android之AppWidgetProvider
- Android音频数据传输
- 调用android系统相机拍照并保存图片