Kotlin

[android : kotlin] 코틀린 웹뷰(WebView) 사용 설정 방법 및 예제 총정리

WebView의 사용법에 대해 알아봅니다. WebView는 앱내에서 웹페이지를 표시하는데 사용됩니다.

코틀린 웹뷰(WebView) 사용 설정

웹뷰(WebView)를 사용하려면 제일 먼저 인터넷 권한을 AndroidMenifest.xml 파일에 추가해야합니다.

<uses-permission android:name="android.permission.INTERNET" />

[AndroidMenifest.xml]

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="edu.kotlin.study">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity3"></activity>
        <activity android:name=".MainActivity2" />
        <activity android:name=".AnotherActivity" />
        <activity
            android:name=".MainActivity"
            android:windowSoftInputMode="adjustPan|adjustUnspecified">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

다음으로 레이아웃에 WebVew를 추가합니다. 웹뷰를 불러오는 동안 로딩바를 추가하기 위해 ProgressBar를 하나 추가합니다.

[activity_main.xml]

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ProgressBar
        android:id="@+id/progress1"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

가장심플한 방법으로 웹뷰를 코드 두줄로 호출할 수 있습니다. 하지만 많은 기능의 제약이 있습니다. loadUrl()메서드를 사용하여 웹사이트를 호출할 수 있습니다.

val webView: WebView = findViewById(R.id.webView1) 
webView.loadUrl("https://ddolcat.tistory.com/")

웹뷰를 제대로 활용하기 위해서는 apply() 메서드를 사용하여 웹뷰에 필요한 설정을 진행합니다.  webViewClient와 webChromeClient 역시 활용해야합니다. 

[MainActivity.kt]

package edu.kotlin.study

import android.annotation.SuppressLint
import android.app.Dialog
import android.content.DialogInterface
import android.graphics.Bitmap
import android.media.MediaPlayer
import android.net.http.SslError
import android.os.Build
import android.os.Bundle
import android.os.Message
import android.view.View
import android.view.ViewGroup
import android.webkit.*
import android.widget.ProgressBar
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {

    private lateinit var webView: WebView
    private lateinit var mProgressBar: ProgressBar

    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        webView = findViewById(R.id.webView1)
        mProgressBar = findViewById(R.id.progress1)

        webView.apply {
            webViewClient = WebViewClientClass() // new WebViewClient()); //클릭시 새창 안뜨게

            //팝업이나 파일 업로드 등 설정해주기 위해 webView.webChromeClient를 설정
            //웹뷰에서 크롬이 실행가능&& 새창띄우기는 안됨
            //webChromeClient = WebChromeClient()

            //웹뷰에서 팝업창 호출하기 위해
            webChromeClient = object : WebChromeClient() {
                override fun onCreateWindow(view: WebView?, isDialog: Boolean, isUserGesture: Boolean, resultMsg: Message?): Boolean {
                    val newWebView = WebView(this@MainActivity).apply {
                        webViewClient = WebViewClient()
                        settings.javaScriptEnabled = true
                    }

                    val dialog = Dialog(this@MainActivity).apply {
                        setContentView(newWebView)
                        window!!.attributes.width = ViewGroup.LayoutParams.MATCH_PARENT
                        window!!.attributes.height = ViewGroup.LayoutParams.MATCH_PARENT
                        show()
                    }

                    newWebView.webChromeClient = object : WebChromeClient() {
                        override fun onCloseWindow(window: WebView?) {
                            dialog.dismiss()
                        }
                    }

                    (resultMsg?.obj as WebView.WebViewTransport).webView = newWebView
                    resultMsg.sendToTarget()
                    return true
                }
            }


            settings.javaScriptEnabled = true
            settings.setSupportMultipleWindows(true) // 새창띄우기 허용여부
            settings.javaScriptCanOpenWindowsAutomatically = true // 자바스크립트 새창뛰우기 (멀티뷰) 허용여부
            settings.loadWithOverviewMode = true //메타태크 허용여부
            settings.useWideViewPort = true //화면 사이즈 맞추기 허용여부
            settings.setSupportZoom(true) //화면 줌 허용여부
            settings.builtInZoomControls = true //화면 확대 축소 허용여부

            // Enable and setup web view cache
            settings.cacheMode =
                WebSettings.LOAD_NO_CACHE //브라우저 캐시 허용여부  // WebSettings.LOAD_DEFAULT
            settings.domStorageEnabled = true //로컬저장소 허용여부
            settings.displayZoomControls = true

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                settings.safeBrowsingEnabled = true  // api 26
            }

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                settings.mediaPlaybackRequiresUserGesture = false
            }

            settings.allowContentAccess = true
            settings.setGeolocationEnabled(true)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                settings.allowUniversalAccessFromFileURLs = true
            }

            settings.allowFileAccess = true
            //settings.loadsImagesAutomatically = true

            fitsSystemWindows = true
        }
 

        val url = "https://ddolcat.tistory.com/"
        webView.loadUrl(url)

   
//
//        //프로그레스 다이얼로그
//        btn5.setOnClickListener {
//
//            val progressBar = ProgressBar(this, null, android.R.attr.progressBarStyleLarge)
//            var params = RelativeLayout.LayoutParams(100, 100)
//            params.addRule(RelativeLayout.CENTER_IN_PARENT)
//            main_layout.addView(progressBar, params)
//
//            // 핸들러를 통해서 종료 작업을 한다.
////            var handler = Handler()
////            var thread = Runnable { pro?.cancel() }
////            handler.postDelayed(thread, 5000) // 딜레이는 5초
//        }
    }
 

    //웹뷰에서 홈페이지를 띄웠을때 새창이 아닌 기존창에서 실행이 되도록 아래 코드를 넣어준다.
    inner class WebViewClientClass : WebViewClient() {
        //페이지 이동
        override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
            view.loadUrl(url)
            return true
        }

        override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
            super.onPageStarted(view, url, favicon)
            mProgressBar.visibility = ProgressBar.VISIBLE
            webView.visibility = View.INVISIBLE
        }

        override fun onPageCommitVisible(view: WebView, url: String) {
            super.onPageCommitVisible(view, url)
            mProgressBar.visibility = ProgressBar.GONE
            webView.visibility = View.VISIBLE
        }


        override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) {
            var builder: android.app.AlertDialog.Builder =
                android.app.AlertDialog.Builder(this@MainActivity)
            var message = "SSL Certificate error."
            when (error.primaryError) {
                SslError.SSL_UNTRUSTED -> message = "The certificate authority is not trusted."
                SslError.SSL_EXPIRED -> message = "The certificate has expired."
                SslError.SSL_IDMISMATCH -> message = "The certificate Hostname mismatch."
                SslError.SSL_NOTYETVALID -> message = "The certificate is not yet valid."
            }
            message += " Do you want to continue anyway?"
            builder.setTitle("SSL Certificate Error")
            builder.setMessage(message)
            builder.setPositiveButton("continue",
                DialogInterface.OnClickListener { _, _ -> handler.proceed() })
            builder.setNegativeButton("cancel",
                DialogInterface.OnClickListener { dialog, which -> handler.cancel() })
            val dialog: android.app.AlertDialog? = builder.create()
            dialog?.show()
        }
    }


}


■HTML 문자열을 로드하는 방법은 loadData()메서드를 사용합니다.

        val tempHtml = "<html><body><h1>또 다른 세계</h1>" +
                "<h1>안녕</h1><h2>만나서 방갑습니다.</h2><h3>오늘은 금요일입니다.</h3>" +
                "<p>웹뷰 사용법에 대해 알아보고 있습니다.</p>" +
                "</body></html>" 
                
        webView.loadData(tempHtml, "text/html", "UTF-8")
        
        webView.loadDataWithBaseURL(null, tempHtml, "text/html", "utf-8", null)

■웹뷰 히스토리를 이용하여 이전페이지와 앞페이지 이동을 할 수 있습니다. 레이아웃에 버튼 두개를 추가하였습니다.

그리고 모든 히스토리를 삭제할 수 있습니다.  clearHistory()메서드를 사용하여 삭제합니다.

webView.clearHistory()
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:layout_above="@+id/btm_layout"/>

    <ProgressBar
        android:id="@+id/progress1"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    
    <LinearLayout
        android:id="@+id/btm_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal">
        <Button
            android:id="@+id/previos_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="이전페이지" />
    
        <Button
            android:id="@+id/next_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="다음 페이지" />
    </LinearLayout>
</RelativeLayout>

웹뷰의 canGoBack()메서드와 canGoForward()메서드를 사용하여 true값이 반환되면 goBack()메서드를 사용하여 이전 페이지로 돌아갈 수 있습니다. goForward()메서드를 사용하여 앞 페이지로 갈 수 있습니다.

        previos_btn.setOnClickListener {
            val canGoBack: Boolean = webView.canGoBack()
            if (canGoBack) {
                webView.goBack()
            }
        }


        next_btn.setOnClickListener {
            val canGoForward: Boolean = webView.canGoForward()
            if (canGoForward) {
                webView.goForward()
            }
        }

■로컬에 있는 html파일을 웹뷰에 보여주는 방법은 loadURL()메서드를 사용합니다.

webView.loadUrl("file:///android_asset/" + "test_sample.html")

assets폴더를 생성하는 방법은 아래글을 확인하세요.

[프로그래밍/Android Studio] – [Android] assets 폴더(folder)를 생성하는 방법: HTML , JSON파일용

■TextView에 HTML문자열을 값으로 설정하고 싶은 경우에는  Html.fromHtml()메서드를 활용합니다.

        val tempHtml = "<html><body><h1>또 다른 세계</h1>" +
                "<h1>안녕</h1><h2>만나서 방갑습니다.</h2><h3>오늘은 금요일입니다.</h3>" +
                "<p>웹뷰 사용법에 대해 알아보고 있습니다.</p>" +
                "</body></html>"
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            textView1.text = Html.fromHtml(tempHtml, Html.FROM_HTML_MODE_LEGACY)
        } else {
            textView1.text = Html.fromHtml(tempHtml)
        }

[build.gradle(Module:app)]

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29

    defaultConfig {
        applicationId "edu.kotlin.study"
        minSdkVersion 22
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.1'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

[build.gradle(Project)]

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    ext.kotlin_version = "1.3.72"
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

[REFERENCE]

WebView에서 웹 앱 빌드

프로그래밍/Android] – [Android] WebView 웹뷰에서 PDF 불러오는 방법

m.blog.naver.com/hando1220/221884820467

developer.android.com/reference/kotlin/android/webkit/WebView

[연관 글]

Leave a Reply

error: Content is protected !!