하이브리드 앱을 만들기위해 웹뷰를 띄우는데, 여러 설정들을 통해 웹뷰의 상태와 상태값들을 알아낼 수 있다.
이전까지만해도 구글링을 통해 나온 블로그들을 참고하여 각 항목들이 어떤 기능을 하는지 명확하지 않은 상태에서 사용하곤 했다. 웹뷰를 많이 다루면서 알아야되는 세팅항목들과 유용하게 사용했던 설정값들을 알아보려한다.
웹뷰를 세팅하는 것은 크게 3가지가 있다.
2번과 3번은 명칭이 비슷해서 헷갈릴 수 있지만 용도가 다르므로 차이를 숙지해야한다.
WebViewClient
는 웹페이지가 로딩될 때 생기는 콜백 함수들로 구성되어있다. 웹 페이지 로딩의 시작과 끝을 알 수 있다.
WebChromeClient
는 웹페이지에서 일어나는 콜백 함수들로 구성되어 있다. 대표적으로 새 창을 띄우거나 파일을 첨부하는 경우다.
웹뷰의 가장 큰 범주에서 웹뷰를 세팅한다.
webView.settings.*로 설정해준다.
settings.apply{
javaScriptEnabled= true // 자바스크립트 사용여부
setSupportMultipleWindows(true) // 새창 띄우기 허용여부
javaScriptCanOpenWindowsAutomatically= true // 자바스크립트가 window.open()을 사용할 수 있도록 설정
loadWithOverviewMode= true // html의 컨텐츠가 웹뷰보다 클 경우 스크린 크기에 맞게 조정
useWideViewPort= true // 화면 사이즈 맞추기 허용여부
setSupportZoom(false) // 화면 줌 허용여부
domStorageEnabled= true // DOM(html 인식) 저장소 허용여부
// 파일 허용
allowContentAccess= true
allowFileAccess= true
mixedContentMode= WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
loadsImagesAutomatically= true
}
WebViewClient
클래스에서 자주 쓰이는 오버라이드 함수는 다음과 같다.
함수명 | 반환 | 설명 |
---|---|---|
shouldOverringUrlLoading | boolean | 웹뷰에서 url이 로딩될 때 호출되며 앱에서 제어할 수 있다. 반환 default는 false이며 로딩 제어시 true를 반환해주어야 한다. |
onPageStarted | void | 페이지가 로딩이 시작되는 시점에 호출된다. |
onPageFinished | void | 페이지가 로딩이 완료되는 시점에 호출된다. |
onReceivedSslError | void | 수신받은 SSL에러가 발생한 경우 호출되며 분기로직을 통해 처리 해준다. |
webview.apply{
...
webViewClient = WebViewClientClass()
...
}
inner class WebViewClientClass : WebViewClient(){
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
(context as MainActivity).loadingCircleDialog.show()
super.onPageStarted(view, url, favicon)
}
override fun onPageFinished(view: WebView?, url: String?) {
(context as MainActivity).loadingCircleDialog.dismiss()
super.onPageFinished(view, url)
}
override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
super.onReceivedSslError(view, handler, error)
handler?.proceed()
val builder: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(context)
var message = "SSL Certificate error"
when (error?.primaryError) {
SslError.SSL_UNTRUSTED -> message = "신뢰할 수 없는 사이트입니다."
SslError.SSL_EXPIRED -> message = "만료된 사이트입니다."
SslError.SSL_IDMISMATCH -> message = "도메인이 없습니다."
SslError.SSL_NOTYETVALID -> message = "검증되지 않은 사이트입니다."
}
message += "페이지로 이동 하시겠습니까?"
builder.setTitle("SSL Certificate Error")
builder.setMessage(message)
builder.setPositiveButton("확인") { _, _ -> handler?.proceed() }
builder.setNegativeButton("취소") { _, _ -> handler?.cancel() }
val dialog: android.app.AlertDialog = builder.create()
dialog.show()
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
Timber.i( "shouldOverrideUrlLoading url: $url")
try {
var intent: Intent? = null
var isKakaoLogin = false
if (url.contains(context.getString(R.string.kakao_intent))) {
intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
val packageManager = context.packageManager
isKakaoLogin = true
if (intent.resolveActivity(packageManager) != null) {
isKakaoLogin = true
}
}
if (intent != null && isKakaoLogin) {
context.startActivity(intent)
} else {
webView.loadUrl(url)
}
} catch (e: Exception) {
Timber.e( e.toString())
}
return true
}
}
WebChromeClient
클래스에서 자주 쓰이는 오버라이드 함수는 다음과 같다.
함수명 | 반환 | 설명 |
---|---|---|
onCreateWindow | boolean | 웹뷰에서 새창이 로딩될 때 호출되며 앱에서 제어할 수 있다. 반환 default는 false이며 로딩 제어시 true를 반환해주어야 한다. |
onCloseWindow | void | 웹뷰가 창을 닫는 시점에 호출된다. |
onPermissionRequest | void | 웹뷰에서 권한을 사용 시 호출되며 권한 사용을 수락할 수 있다. |
onShowFileChooser | boolean | 파일을 웹으로 전송할 수 있다. 반환값을 true설정하고 인텐트를 통해 데이터를 전송한다. |
getDefaultVideoPoster | bitmap | 플레이어 화면을 로딩할 때 디폴트 포스터가 노출된다. 반환 값을 수정해서 포스터를 없앨 수 있다. |
var uploadMessage: ValueCallback<Array<Uri?>>? = null
...
webview.apply{
...
webChromeClient = WebChromeClientClass()
...
}
inner class WebChromeClientClass : WebChromeClient() {
override fun onCreateWindow(view: WebView?, isDialog: Boolean, isUserGesture: Boolean, resultMsg: Message?): Boolean {
Timber.i( "onCreateWindow url")
val url = view?.url
if (url != null) {
Timber.i("new load url: $url")
}
val newWebView = WebView(context).apply {
settings.run {
javaScriptEnabled = true
setSupportMultipleWindows(false)
}
}
newWebView.webChromeClient = object : WebChromeClient() {
override fun onCloseWindow(window: WebView?) {}
}
(resultMsg?.obj as WebView.WebViewTransport).webView = newWebView
resultMsg.sendToTarget()
return true
}
override fun onPermissionRequest(request: PermissionRequest?) {
Timber.e( "onPermissionRequest")
try {
request?.grant(request.resources)
} catch (e: Exception) {
Timber.e( "permissionRequest: $e")
}
}
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri?>>,
fileChooserParams: FileChooserParams
): Boolean {
if(uploadMessage != null){ // 값이 존재하면 널값을 넣어 초기화해주어야 한다.
uploadMessage!!.onReceiveValue(null)
}
uploadMessage = filePathCallback
val intent = Intent()
intent.apply {
action = Intent.ACTION_GET_CONTENT
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
(this@WebViewSetting.context as MainActivity).requestActivity.launch(Intent.createChooser(intent, "File Chooser"))
return true
}
override fun getDefaultVideoPoster(): Bitmap? {
return Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
}
}
파일선택의 경우 인텐트에 파일 카테고리와 타입을 설정하고 미리 선언한 requestActivity
변수를 통해 선택한 파일을 전송한다.
WebViewClient()
의 shouldOverrideUrlLoading
와 WebChromeClient()
의 onCreateWindow
가장 큰 차이는 새탭이 열리는지의 유무이다.