티스토리 뷰

반응형

AndroidManifest 작성

 

Kakao 도로명 주소는 웹뷰를 통해 불러오기 때문에 인터넷 권한이 필요합니다.

 

따라서 manifest에 아래 코드를 작성합니다.

 

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest 
    ... >

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

    <application
        ...
        android:usesCleartextTraffic="true"
        
        ...
    </application>

</manifest>

 

 

XML 코드 작성

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    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"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="40sp">

        <EditText
            android:id="@+id/location_editText"
            android:layout_width="match_parent"
            android:layout_height="50sp"
            android:editable="false"
            android:focusable="false"
            android:textSize="10sp"
            android:hint="주소"/>

        <EditText
            android:id="@+id/detail_location_editText"
            android:layout_width="match_parent"
            android:layout_height="50sp"
            android:layout_marginTop="40sp"
            android:textSize="10sp"
            android:hint="상세주소"/>

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 

주소와 상세주소를 입력받을 수 있는 디자인으로 구성해보았습니다.

 

 

주소 부분을 클릭하면 도로명 주소 입력 창을 보여주고 결과 값을 받아오려고 합니다.

주소를 입력받는 부분을 EditText로 생성을 하였습니다. 

따라서 editable을 false로 지정해 수정할 수 없게 하였고, focusable도 false로 주어 첫 번째 클릭 시 바로 창을 띄어주게 하였습니다.

 

focusable을 false로 주지 않으면 두 번째 클릭 시에 창이 열리게 되는 현상이 발생합니다. (EditText는 기본적으로 focusable이 있음)

 

 

 

 

도로명 주소를 띄어줄 창 구현

카카오 도로명 주소 api는 직접 서버에 올리고 웹뷰로 받아와야 됩니다.

 

저는 ssh로 접속해 파일을 생성하고 아래 코드를 작성해주었습니다.

 

<?php
header("Content-Type: text/html; charset=UTF-8");
?>
<script src="http://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<script>
    new daum.Postcode({
        oncomplete: function(data) {
            if(data.userSelectedType=="R"){

                window.Leaf.getAddress(data.zonecode, data.roadAddress, data.buildingName);
            }
            else{
                window.Leaf.getAddress(data.zonecode, data.jibunAddress, data.buildingName);
            }      
        }
    }).open();
</script>

userSelectedType이 구주소인지, 신주소인지 체크하여 data를 넘겨주는 코드입니다.

window. 다음에 Leaf는 앱과 통신하기 위한 name이고, getAddress는 앱에서 사용하는 JavascriptInterface의 함수 이름입니다.

 

name과 함수 이름은 원하는 대로 수정해주시면 됩니다. (앱 소스 코드도 수정해주셔야 됩니다.)

 

 

리눅스로 생성한 서버면 아래와 같이 생성해주면 됩니다.

 

  1.  cd www (www로 접속)
  2. vi "생성할 파일 이름".php
  3. i로 insert 지정 후 내용 입력
  4. esc 누르고 :wq 입력 후 엔터

 

그리고 인터넷에 http://내 서버 주소/생성한 파일 이름. php로 접속을 하면 아래와 같이 도로명 주소 창이 뜨는 것을 확인할 수 있습니다.

 

 

 

코드 작성하기

먼저 전체 소스 코드입니다.

class MainActivity : AppCompatActivity() {

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

        location_editText.setOnClickListener {
            showKakaoAddressWebView()
        }
    }

    private fun showKakaoAddressWebView() {

        webView.settings.apply {
            javaScriptEnabled = true
            javaScriptCanOpenWindowsAutomatically = true
            setSupportMultipleWindows(true)
        }

        webView.apply {
            addJavascriptInterface(WebViewData(), "php에서 적용한 name")
            webViewClient = client
            webChromeClient = chromeClient
            loadUrl("내 주소")
        }
    }

    private val client: WebViewClient = object : WebViewClient() {

        override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
            return false
        }

        override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
            handler?.proceed()
        }
    }

    private inner class WebViewData {
        @JavascriptInterface
        fun getAddress(zoneCode: String, roadAddress: String, buildingName: String) {

            CoroutineScope(Dispatchers.Default).launch {

                withContext(CoroutineScope(Dispatchers.Main).coroutineContext) {

                    location_editText.setText("($zoneCode) $roadAddress $buildingName")

                }
            }
        }
    }

    private val chromeClient = object : WebChromeClient() {

        override fun onCreateWindow(view: WebView?, isDialog: Boolean, isUserGesture: Boolean, resultMsg: Message?): Boolean {

            val newWebView = WebView(this@MainActivity)

            newWebView.settings.javaScriptEnabled = true

            val dialog = Dialog(this@MainActivity)

            dialog.setContentView(newWebView)

            val params = dialog.window!!.attributes

            params.width = ViewGroup.LayoutParams.MATCH_PARENT
            params.height = ViewGroup.LayoutParams.MATCH_PARENT
            dialog.window!!.attributes = params
            dialog.show()

            newWebView.webChromeClient = object : WebChromeClient() {
                override fun onJsAlert(view: WebView, url: String, message: String, result: JsResult): Boolean {
                    super.onJsAlert(view, url, message, result)
                    return true
                }

                override fun onCloseWindow(window: WebView?) {
                    dialog.dismiss()
                }
            }

            (resultMsg!!.obj as WebView.WebViewTransport).webView = newWebView
            resultMsg.sendToTarget()

            return true
        }
    }
}

 

 

showKakaoAddressWebView 함수

webView.settings.apply {
    javaScriptEnabled = true
    javaScriptCanOpenWindowsAutomatically = true
    setSupportMultipleWindows(true)
}

webView.apply {
    addJavascriptInterface(WebViewData(), "php에서 적용한 name")
    webViewClient = client
    webChromeClient = chromeClient
    loadUrl("내 주소")
}

 

카카오 도로명 주소 api는 새창으로 도로명 주소가 뜨기 때문에 multipleWindows를 true로 설정해주었습니다.

 

그밖에

결과 값을 받아오기 위한 addJavascriptInterface와

webView에서 이뤄지는 것들을 관리하는 webViewClient,

새창을 띄어주기 위한 webChromeClient를 지정해주었습니다.

 

내 주소 부분에는 서버로 올린 파일의 링크를 작성하시면 됩니다. 

 

WebViewData class

private inner class WebViewData {
    @JavascriptInterface
    fun getAddress(zoneCode: String, roadAddress: String, buildingName: String) {

        CoroutineScope(Dispatchers.Default).launch {
        
            withContext(CoroutineScope(Dispatchers.Main).coroutineContext) {

                location_editText.setText("($zoneCode) $roadAddress $buildingName")

            }
        }
    }
}

 

결과 값을 받아오기 위한 WebViewData class입니다.

 

@JavascriptInterface 어노테이션을 붙이고 서버에서 지정해줬던 함수 이름을 똑같이 생성해줍니다.

 

비동기 처리가 필요해서 저는 Coroutine을 사용했지만, Handler를 사용해도 됩니다.

 

 

client

 private val client: WebViewClient = object : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
        return false
    }

    override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
        handler?.proceed()
    }
}

 

도로명 주소 화면이 불러와지지 않는 오류를 방지하기 위해서 WebViewClient 관련 코드를 작성해주었습니다.

 

 

private val chromeClient = object : WebChromeClient() {

    override fun onCreateWindow(view: WebView?, isDialog: Boolean, isUserGesture: Boolean, resultMsg: Message?): Boolean {

        val newWebView = WebView(this@MainActivity).apply {
            settings.javaScriptEnabled = true
        }

        val dialog = Dialog(this@MainActivity)

        dialog.setContentView(newWebView)

        val params = dialog.window!!.attributes.apply {
            width = ViewGroup.LayoutParams.MATCH_PARENT
            height = ViewGroup.LayoutParams.MATCH_PARENT
        }
        
        dialog.window!!.attributes = params
        dialog.show()

        newWebView.webChromeClient = object : WebChromeClient() {
            override fun onJsAlert(view: WebView, url: String, message: String, result: JsResult): Boolean {
                super.onJsAlert(view, url, message, result)
                return true
            }

            override fun onCloseWindow(window: WebView?) {
                dialog.dismiss()
            }
        }

        (resultMsg!!.obj as WebView.WebViewTransport).webView = newWebView
        resultMsg.sendToTarget()

        return true
    }
}

 

도로명 주소가 새창으로 띄어지기 때문에 새로운 웹뷰를 생성 후 보여주는 코드를 작성해주었습니다.

위와 같이 작성해주면 dialog 형식으로 보이게 됩니다.

 

 

 

 

소스 코드 : github.com/Im-Tae/Blog_Example/tree/master/Android/KakaoAddressExample

반응형
댓글