코틀린 안드로이드 - 웹 브라우저(네비게이션 기능, 완성도 높이기)

Jamwon·2021년 8월 17일
0

Kotlin_Android

목록 보기
24/30

웹뷰의 네비게이션 기능에는 홈으로가기 앞으로 가기 뒤로가기가 있다.

이전에 만들어놨던 버튼들을 프로퍼티로 선언해주고 bindViews에서 기능을 만들어주자!

gobackButton은 webView.goBack() 을 이용해서 간단하게 만들수 있다.

goForwardButton 은 webView.goForward()를 이용!

그리고 안드로이드 폰에서 지원하는 내장 backButton에 뒤로가기 기능을 탑재해준다.

onBackPress()

오버라이드 해서 사용함는 onBackPress() 내장되어있는 뒤로가기 버튼을 눌렀을때 행하는 action을 정의할 수 있다.

여기서 super.onBackPressed()는 기본 내장 뒤로가기버튼의 기능!

webView.canGoBack() 을 사용하면 뒤로갈수 있는지 없는지를 반환한다.
뒤로 갈 수 있다면 webView.goBack()을 아니면 기존 뒤로가기 버튼의 기능을 수행한다.

홈버튼을 위해서 홈 url을 저장할 수 있는 별도의 상수를 정의한다!

    companion object{
        private const val DEFAULT_URL ="http://www.google.com"
    }

위처럼 초기 URL을 구글로 설정! 나중에 홈페이지를 바꾸고 싶다면 이것만 바꾸면된다.

        goBackButton.setOnClickListener {
            webView.goBack()
        }

        goForwardButton.setOnClickListener {
            webView.goForward()
        }
        goHomeButton.setOnClickListener {
            webView.loadUrl(DEFAULT_URL)
        }

이처럼 간단한게 네비게이션 관련 기능을 만들 수 있다!

UI 완성도 높이기

ClickEvent

버튼을 클릭할시에 ripple effect를 준다!

android:background="?attr/selectableItemBackground"

?attr - 사전에 정의되어있는 속성을 사용할때 사용
버튼에 지정해주면 누를때 ripple effect가 생긴다.

하지만 위의 방식처럼 지정해주면 constraint layout에 의해 버튼이 작아지게 되는데 이때 버튼 layout의 width와 height을 모두 match_constraint(0dp)로 주고

app:layout_constraintDimensionRatio="1:1"

로 지정해주면 현재 top과 bottom의 constraint가 parent에 맞춰져있기 때문에 1:1 비율로 꽉차게 다시 버튼이 위치 한다!

3개의 navigation 버튼에
위의 두개 속성을 지정해주면 클릭시 ripple effect와 더욱 깔끔해진걸 볼수있다 !

AddressBar 꾸미기

주소창을 우리가 익숙한 형태로 꾸며본다!

밝은 회색과 어두운 회색을 res/values/colors.xml에 정의해준다.

둥근 주소창을 만들기 위해서 res/drawable 에 new drawable resource file을 shape로 만들어 준다.

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/light_gray"/>
    <corners android:radius="16dp"/>
</shape>

위와 같이 만들어준다.!

위와같은 shape를 EditText의 background로 사용하면 커서가 그대로 영역의 맨 좌측에 위치한다. 따라서

android:paddingHorizontal="16dp"

Horizontal padding값을 부여해서 커서의 시작점이 잘 맞게 해준다.


위처럼 이쁘게 나온다!

elevation

하지만 주소창과 WebView가 구분이 되지 않기 때문에
toolbar 영역 (constraintLayout)에

android:elevation="4dp"

를 부여한다. 이때 constraintLayout은 배경색이 없기때문에 elevation을 지정해줘도 아무런 변화가 없기 때문에 background를 지정해줘야된다.

잘 구분된다!

Refresh 기능

Swiperefreshlayout

공식문서

swipe해서 새로고침 UI패턴을 구현할때 사용된다.

종속항목으로 build.gradle에

dependencies {
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
}

종속 항목을 추가해준다.

Swiperefreshlayout은 스크롤이 가능한 영역을 감싸면서 구현이 된다고 한다!

따라서 웹뷰를 Swiperefreshlayout으로 감싸준다!

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    	android:id="@+id/refreshLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar">

        <WebView
            android:id="@+id/webView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

이런식으로 작성!


위처럼 위에서 밑으로 swipe하면 빙글빙글 로딩 아이콘이 뜨지만 아무일도 일어나지 않는다!

    private val refreshLayout: SwipeRefreshLayout by lazy {
        findViewById(R.id.refreshLayout)
    }

이처럼 프로퍼티 선언도 해주고 bindViews()에

        refreshLayout.setOnRefreshListener {
            webView.reload()
        }

위와 같이 setOnRefreshListener를 이용해서 refresh기능을 완성해준다. 하지만 refresh해도 로딩 아이콘은 사라지지가 않는다!!

이걸 사라지게 할려면 refreshLayout의 isRefreshing 프로퍼티를 false로 바꿔줘야 사라진다!

따라서 직접 웹뷰클라이언트를 상속받아서 웹뷰의 load가 끝났을때 isRefreshing을 false로 바꿔준다.

inner class를 사용함으로써 refreshLayout에 접근할수 있게 만들어준다.

    inner class WebViewClient : android.webkit.WebViewClient() {
        override fun onPageFinished(view: WebView?, url: String?) {
            super.onPageFinished(view, url)

            refreshLayout.isRefreshing = false
        }
    }

WebViewClinet를 상속받아서 onPageFinished를 사용!
PageFinished = 로딩완료 -> isRefreshing = false로 해서 로딩아이콘?을 사라지게 해준다.

ContentLoadingProgressBar

공식문서

로딩바가 떳을때 이게 떳다가 사라지는걸 사용자가 인지할수 있게 (가변적인 로딩바에 대해서) 해주는 위젯.

ProgressBar를 상속받는다.

    <androidx.core.widget.ContentLoadingProgressBar
        android:id="@+id/progressBar"
        android:layout_width="0dp"
        android:layout_height="2dp"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        app:layout_constraintBottom_toBottomOf="@id/toolbar"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"

        />

위와같이 추가! view의 최상단에 위치해야 되기때문에 xml 파일 제일 하단에 위치해준다.

webChromeClinet

얼마나 로딩됬는지 알기 위해서는 webChromeClient가 필요하다.

webView에서 지원하지 않는 기능들을 WebViewClient나 WebChromeClinet를 이용해서 사용할수 있다고 한다.

여기서 onProgressChanged를 사용!
파라미터로 page의 로딩 progress를 0~100사이의 integer로 받는다고 한다.

    private fun initViews() {
        webView.apply {
            webViewClient = WebViewClient()
            webChromeClient = WebChromeClient()
            settings.javaScriptEnabled = true
            loadUrl(DEFAULT_URL)
        }

    }

initViews()에 webChormeClient를 추가해주고

    inner class WebChromeClient: android.webkit.WebChromeClient(){

        override fun onProgressChanged(view: WebView?, newProgress: Int) {
            super.onProgressChanged(view, newProgress)
            progressBar.progress = newProgress
        }
    }

위와 같이 progressBar의 progress를 로딩상태에 따라 보여준다!!

그리고 progressBar가 페이지의 로딩이 시작될때와 로딩이 끝나기 전까지만 보여야 하기 때문에 그것은 WebViewClient에서 처리해준다.

    inner class WebViewClient : android.webkit.WebViewClient() {
        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            super.onPageStarted(view, url, favicon)
            progressBar.show()
        }


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

            refreshLayout.isRefreshing = false
            progressBar.hide()
        }
    }

위와같이 onPageStarted에서 progressBar를 show()하고 onPageFinished에서 hide() 해준다.

history가 없을때 backButton 비활성화하기

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

            refreshLayout.isRefreshing = false
            progressBar.hide()
            goBackButton.isEnabled= webView.canGoBack()
            goForwardButton.isEnabled = webView.canGoForward()
        }

페이지 로딩이 끝났을때 버튼의 isEnabled 값을 webView.canGoBack()을 사용해서 지정해준다. 강사님은 이런걸 다알고있다니!!

앞,뒤 버튼 모두 적용!

redirecting 주소 보여주기

onPageFinished에서는 로딩이 끝나면 view와 url을 반환하는데 로딩이 끝나면 addressBar의 text를 그 url로 지정해두면 로딩이 끝나고 리다이렉팅된 url을 표시해준다.

addressBar.setText(url)

android:selectAllOnFocus="true"

EditText 속성잘 정리된 블로그
EditText가 focus될 때 EditText의 모든 텍스트를 자동선택해주는 옵션이다.

주소입력시 http 자동으로 붙여주기

URLUtil.isNetworkUrl()

인자값이 httpUrl이거나 httpsUrl이면 ture를 반환하고 둘다없으면 false를 반환한다,

    private fun bindViews() {
        addressBar.setOnEditorActionListener { v, actionId, event ->
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                val loadingUrl = v.text.toString()
                if(URLUtil.isNetworkUrl(loadingUrl)){
                    webView.loadUrl(loadingUrl)
                } else{
                    webView.loadUrl("http://$loadingUrl")
                }

            }
            return@setOnEditorActionListener false
        }

따라서 http나 https url이 입력되어있으면 그냥 load하고 그게 아니라면 앞에 http를 붙여준 다음에 load 한다!!

마지막으로 theme에 DarkActionBar를 NoActionBar로 변경해서 맨위의 ActionBar를 없에준다.

style name="Theme.WebView" parent="Theme.MaterialComponents.DayNight.NoActionBar">

        <item name="android:statusBarColor" tools:targetApi="l">@color/transparent</item>
        
        <item name="android:windowLightStatusBar">true</item>

transparent 색깔을 #00000000 로 지정해주고 LightStatusBar 값을 true로 줘서 상태바의 아이콘들의 색깔이 어둡게 바뀌게 해준다.

위와같이 잘 나타나고 다른 것들도 잘된다!

새로 배운것

WebView

webView의 goBack() , goForward() 함수 사용 뒤로가기 앞으로 가기 기능이 함수로 미리 만들어져 있어서 간단!

ClickEvent

android:background="?attr/selectableItemBackground"
로 버튼을 누를때 ripple effect를 줄 수 있다.

또한 버튼의 layout를
app:layout_constraintDimensionRatio="1:1"
를 이용해서 크기를 조절할 수 있다.

elevation

android:elevation="4dp" 와같이 사용해서 그림자를 나타낼수 있다.
background에 color가 존재할때 보인다!!

SwiperefreshLayout

dependencies {
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
}

스크롤이 가능한 영약을 감싸서 만들수 있다.

        refreshLayout.setOnRefreshListener {
            webView.reload()
        }

refreshLayout.isRefreshing = false

위와같이 코드를 작성하여서 swipe down했을때의 action과 특정상황에서 isRefeshing = false를 지정해서 로딩 아이콘을 끌 수있다.

ContentLoadingProgressBar

ProgressBar를 상속받은 객체로 로딩바를 보여주는 위젯

webChromeClient

webView에서 지원하지 않는 기능을 사용하기 위한 Client이다.
여기서는 페이지가 얼마나 로딩되었는지 알기위해 사용되었다.

    inner class WebChromeClient: android.webkit.WebChromeClient(){

        override fun onProgressChanged(view: WebView?, newProgress: Int) {
            super.onProgressChanged(view, newProgress)
            progressBar.progress = newProgress
        }
    }

위와같이 사용되었다.

webViewClient

webChromeClient와 비슷한 개념으로 이 프로젝트에서는 페이지의 로딩이 시작됬을때와 페이지의 로딩이 끝났을때의 action을 지정해줄 때 사용되었다.

URLUtil.isNetworkUrl()

인자값이 httpUrl이거나 httpsUrl이면 ture를 반환하고 둘다없으면 false를 반환한다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
간단한 웹뷰인것 같은데도 조금조그만한 요소들이 진짜 많다!!! 와!!!
EditText의 속성들이라던지 진짜 android에는 너어어무나 많은 것들이 있으니 시간이 날때마나 공식문서를 열심히 읽는게 중요한것 같다!

끝!

profile
한걸음씩 위로 자유롭게

0개의 댓글