티스토리 뷰

728x90

 

 

2022.04.11 - [안드로이드/코드] - 안드로이드 웹뷰 사용해보기 1 : 웹 사이트 제작

 

안드로이드 웹뷰 사용해보기 1 : 웹 사이트 제작

평소에 웹뷰는 앱 내에서 인터넷 보여주는 용도로만 사용하고 그 외는 잘 사용하지 않았었는데 이번에 학습하면서 조금 더 깊게 알아보려고 합니다. 1. 학습 목표 웹뷰 기본 사용 법 웹 안드로이

alanboyce.tistory.com

이전 글에서 만든 웹페이지를 이용하여 웹뷰를 사용해 보겠습니다.

 

1. 웹뷰 기본 사용 방법

시작하기 앞서 기본적으로 웹뷰 사용 방법에 대해 알아보겠습니다.

제가 사용할 예제에서는 필요 없지만 기본 사용법을 알기 위해서 Manifests에 인터넷 퍼미션을 추가해 줍니다.

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

이제 xml에서 WebView를 생성해줍니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

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

</androidx.constraintlayout.widget.ConstraintLayout>

이제 액티비티로 넘어와줍니다.

private lateinit var webView: WebView

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

        webView = findViewById(R.id.webView)

        webView.loadUrl("https://m.naver.com")

    }

본인이 원하는 주소를 loadUrl에 넣어주기만 하면 기본적인 웹뷰 사용은 끝입니다!
결과는 예상하는 대로 네이버 화면이 나오므로 스크린샷은 생략하겠습니다.

다음에는 사용 빈도는 어떤지 잘 모르겠지만 코드 안에서 웹 코드를 직접 입력한 뒤 화면에 표시하는 방법입니다.

val document = "<!DOCTYPE html><head>웹뷰 테스트</head>" +
                    "<body><h1>웹뷰 테스트!!</h1>직접 넣는 방식으로 만들어 봤습니다.</body></html>"
webView.loadData(document, "text/html; charset=utf-8", "UTF-8")

다음에는 제가 이번 예제에서 진행하려고 하는 방식입니다.

webView.apply {
    settings.javaScriptEnabled = true
    loadUrl("file:///android_asset/login.html")
}

지난 글에서 만든 웹을 assets 폴더로 넣어 놨기 때문에 다음과 같이 "file:///android_asset/" 하위에 있는 파일을 탐색하여 가져올 수 있습니다.
추가로 저희는 이번 예제에서 javascript를 활용할 예정이기 때문에 javaScriptEnabled를 true로 설정해 주셔야 합니다.

 

2. 로그인 화면

시작하기 전에 진행 로직을 먼저 설명드리겠습니다.

1. 로그인 페이지 로드
2. App의 SharedPreferences에 id, pw정보 있는지 조회
2-1. id, pw 정보가 있을 경우 Web페이지에 id, pw정보 넘겨서 화면에 표시
3. 로그인 버튼 클릭
3-1. id 또는 pw 정보를 입력하지 않았을 경우 오류 다이얼로그 표시
4. id, pw 정보를 App에 전달, 전달받은 정보를 SharedPreferences에 저장
5. 로그인에 사용한 id, pw 정보를 토스트 메시지로 출력
6. 다음 화면으로 이동

이번 에제에서는 로그인 로직이 중요한 게 아니기 때문에 로그인 성공 여부 조회라던지 글자 수 제한 등 이러한 요소는 전혀 없이
id, pw에 내용이 있는지 없는지만 판단하는 걸로 할 예정입니다.

우선 login.xml에 javascript 내용을 추가하겠습니다.

<script>
    function login(){
        var id = document.getElementById("login_id").value;
        var pw = document.getElementById("login_pw").value;
        if(id == null || pw == null || id == "" || pw == "") {
            alert("아이디 또는 패스워드를 입력해주세요.")
        } else {
            showAndroidToast("id : " + id + " / pw : " + pw);
            currentLoginInfo(id, pw);
            window.location = "file:///android_asset/select.html";
        }
    }

    function getLoginInfo(id, pw) {
        document.getElementById("login_id").value = id;
        document.getElementById("login_pw").value = pw;
    };

    function currentLoginInfo(id, pw) {
        Android.currentLoginInfo(id, pw);
    };

    function showAndroidToast(toast){
        Android.showToast(toast);
    }

</script>

login 함수는 단순 javaScript함수이고 나머지 함수들은 App과 연계해서 사용할 예정이므로
App코드 설명할 때 자세히 다루겠습니다.

1) web에서 사용하는 콘솔 로그와 알럿 다이얼로그 표시

사실 콘솔 로그의 경우 별다른 조치 없이도 볼 수는 있습니다만,

[INFO:CONSOLE(36)] "로그인 시도", source: file:///android_asset/login.html (36)

제가 알고 싶은 건 "로그인 시도"라는 문구만 보고 싶은데 다른 불필요한 요소가 같이 나옵니다.
이럴 경우에는 다음과 같은 방식으로 원하는 내용만 확인이 가능합니다.

class MyWebChromeClient(private val context: Context) : WebChromeClient() {

    override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
        Log.e("WebViewLog", "${consoleMessage?.message()}")
        return super.onConsoleMessage(consoleMessage)
    }
    
}

위와 같이 WebChromClient를 상속받는 클래스를 생성 후 onConsoleMessage를 override 해줍니다.
저는 web에서 등록한 로그만 확인하고 싶었기 때문에 message만 사용하였는데

consoleMessage?.messageLevel()
consoleMessage?.lineNumber()

필요에 따라 messageLevel, lineNumber 등을 활용해서 로그를 꾸며주시면 됩니다.

E/WebViewLog: 로그인 시도

적용 시에 logcat에서 다음과 같이 잘 나오는 것을 확인할 수 있습니다.

webview.webViewClient = MyWebClient()

만든 클래스는 웹뷰에서 위와 같이 등록이 가능합니다.

다음은 Alert 다이얼로그입니다. 기본적으로 WebChromeClient를 등록하지 않으면 다이얼로그는 표시되지 않는 걸로 확인됩니다.
WebChromeClient를 등록했다고 하더라도 아무것도 하지 않았을 경우 다음 이미지처럼 표시가 됩니다.

이번 예제에서는 다이얼로그 커스텀은 진행하지 않을 예정이지만 이번 예제를 응용하면 앱 스타일에 맞게 커스텀 다이얼로그로 표시도 가능합니다.

class MyWebChromeClient(private val context: Context) : WebChromeClient() {

    ...

    override fun onJsAlert(
        view: WebView?,
        url: String?,
        message: String?,
        result: JsResult?
    ): Boolean {
        AlertDialog.Builder(context)
            .setTitle("알림")
            .setMessage(message)
            .setPositiveButton("확인") { dialog, _ ->
                dialog.dismiss()
            }
            .setCancelable(false)
            .show()

        result?.cancel()
        return true
    }
}

사용 방법은 위와 같으며 주의할 점은 result?.cancel() 이 부분입니다.
이 부분을 입력하지 않으면 앱에서 띄운 다이얼로그와 위 사진에 나온 웹에서 띄운 다이얼로그가 2개가 발생할 수 있습니다.
그러므로 웹에서 띄우는 것은 종료시키는 게 좋습니다.

이 두 가지 외에도 WebChromeClient에서는 다양한 기능을 제공하고 있습니다.

상황에 맞게 필요한 것을 찾아서 적용해보세요~ㅎㅎ

 

2. 웹뷰 상태를 활용하여 데이터 전송하기

위에서 WebChromeClient를 알아봤다면 이번에는 WebClient를 알아보겠습니다.

WebClient에서는 위와 같이 웹뷰의 여러 가지 상태 값을 알 수 있습니다.
이 중에서 이번 예제에서는 웹뷰가 로드 완료되었을 때 로그인 이력이 있을 경우 아이디 패스워드 자동 입력을 진행해 볼 것입니다.

class MyWebClient : WebViewClient() {

    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)

        view?: return

        if (url?.contains("login.html") == true) {
            val id = MyPreference(view.context).getString(MyPreference.ID, "")
            val pw = MyPreference(view.context).getString(MyPreference.PW, "")

            if (id.isNotEmpty() && pw.isNotEmpty()) {
                view.evaluateJavascript("javascript:setLoginInfo('$id', '$pw');") {}
            }
        }
    }
}

웹뷰에서 페이지 로드가 완료되게 되면 onPageFinished과 호출되어서 아래의 내용을 진행하게 됩니다.
SharedPreference에서 id, pw 값을 가져왔다면 이제 App에서 데이터를 Web 쪽으로 넘겨줍니다.

view.evaluateJavascript("javascript:setLoginInfo('$id', '$pw');") {}

App에서는 evaluateJavascript 함수를 통해 데이터를 넘겨주게 되고

function setLoginInfo(id, pw) {
    document.getElementById("login_id").value = id;
    document.getElementById("login_pw").value = pw;
};

Web에서는 Javascript를 통해 위와 같이 데이터를 받아서 id, pw 내용을 채웁니다.

만든 WebClient등록 방법은 아래와 같습니다.

webview.webViewClient = MyWebClient()

 

3. JavascriptInterface를 활용하여 데이터 받기

class MyWebAppInterface(
    private val mContext: Context,
    private val moveActivity : (Int) -> Unit
) {
    @JavascriptInterface
    fun showToast(message: String) {
        Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show()
    }

    @JavascriptInterface
    fun currentLoginInfo(id: String, pw: String) {
        MyPreference(mContext).setString(MyPreference.ID, id)
        MyPreference(mContext).setString(MyPreference.PW, pw)
    }

    @JavascriptInterface
    fun selectCharacter(index: Int) {
        moveActivity(index)
    }
}

이번에는 따로 상속을 받을 필요는 없고 함수를 만들 때 @JavascriptInterface 어노테이션을 달아주시면 됩니다.

webview.addJavascriptInterface(
    MyWebAppInterface(this@MainActivity) { moveActivity(it) },
    "Android"
)

...
private fun moveActivity(index : Int) {
    val intent = Intent(this, SubActivity::class.java).also {
        it.putExtra(SubActivity.SELECT_KEY, index)
    }
    startActivity(intent)
}

등록은 위와 같이하면 되고 여기서 중요한 부분은
addJavascriptInterface의 두 번째 인자인 "Android"입니다.
이 이름이 웹의 javascript에서 사용되기 때문에 정확하게 입력하여야 합니다.

function currentLoginInfo(id, pw) {
    Android.currentLoginInfo(id, pw);
};

function showAndroidToast(toast){
    Android.showToast(toast);
}

javascript에서는 위에서 만든 "Android"를 통해 데이터 전달이 가능해지고
앱에서는 JavascriptInterface를 통해 데이터를 받아서 이용이 가능합니다.

 

내용이 길어져서 3부에서는 웹뷰의 속성들에 대하여 알아볼 예정입니다.
전체 코드의 경우에는 3부까지 마치고 주석 추가 후 깃허브에 올리겠습니다~

2022.04.16 - [안드로이드] - 안드로이드 웹뷰 사용해보기 3: 각종 속성들 알아보기

 

안드로이드 웹뷰 사용해보기 3: 각종 속성들 알아보기

2022.04.13 - [안드로이드/코드] - 안드로이드 웹뷰 사용해보기 2 : 웹뷰 적용하기 안드로이드 웹뷰 사용해보기 2 : 웹뷰 적용하기 2022.04.11 - [안드로이드/코드] - 안드로이드 웹뷰 사용해보기 1 : 웹 사

alanboyce.tistory.com

 

728x90
댓글