이번에는 간단한 웹브라우저를 만들었다. 보기에는 진짜 크롬같다는 생각이 먼저 든다. 만들기 전에 내가 하나하나 다 구현해야 할 줄 알았는데, 이미 존재하는 웹브라우저(크롬)을 상속받아서 기능을 구현했다. 뭐 특별히 엄청난 코드를 작성하고, 네트워크쪽이나 보안을 신경쓰면서 만든 것은 아니지만, 이런 기능이 있다는 것이 신기했다. 또한 WebView를 처음으로 써봤는데 신기했다.
# 결과물 미리보기
홈버튼, 뒤로가기 버튼, 앞으로가기 버튼, 주소입력창과 스크롤하여 새로고침 기능을 구현했다. 우리가 사용하는 브라우저처럼 검색했을때 주소가 주소창에 계속 바뀌는 기능과, 앞으로가기 뒤로가기버튼도, 갈 곳이 없다면 비활성화 되게 구현했다. 또한 핸드폰자체의 뒤로가기 버튼도 브라우저의 뒤로가기 버튼효과가 나도록 했다.
# 알게 된 것
- WebView
- SwipeRefreshLayout
- ContentLoadingProgressBar
- 핸드폰 뒤로가기 버튼 재정의
- EditText - imeOptions
# WebView
요약하면 주소표시줄 탐색컨트롤과 같이 브라우저에서 제공해주는 여러 편의기능을 제외하고, 오직 애플리케이션위에 웹페이지를 띄워주는 역할을 하는 것이라고 생각하면 좋을거 같다.
## WebView ( XML )
XML은 별다른 특이점 없이 크기를 지정해주면 된다.
## WebView 사용하기
xml의 view를 연결 시켜 준 후
private val webView: WebView by lazy {
findViewById(R.id.webView)
}
webViewClient와 webChromeClient를 만들어 준다. 각각의 Client는 어느정도 깊은 구현사항을 요구하느냐에 따라서 달라지는데, 정말 간다한건 webViewClinet만 사용할 수 있고, Chrome에서 사용하는거와 같은 기능들은 ChromeClinet를 사용해야한다. 강의에서는 해당 클라이언트를 inner Class로 별도로 정의했지만, 난 익명객체를 만드는 방법을 공부했었기 때문에 응용해볼 겸 사용했다.
webView안에 속성(property)인 webViewClient에 별다른 값을 지정해주지 않으면 내가 정의한 Client을 사용하지 않고 기본 브라우저인 Chrome이 열리는 것을 확인 할 수 있었다.
private fun initWebView() {
webView.apply {
// 페이지가 끝났을때 특정 이벤트를 정의하기 위해 익명객체 생성
webViewClient = object : 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()
backButton.isEnabled = webView.canGoBack()
forwardButton.isEnabled = webView.canGoForward()
uriEditText.setText(url)
}
}
//로딩 창 구현 이벤트를 정의하기 위해 익명객체 생성
webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
progressBar.progress = newProgress
}
}
settings.javaScriptEnabled = true
loadUrl(DEFAULT_URI)
}
uriEditText.setText(DEFAULT_URI)
}
이후 웹 뷰에서도 자바스크립트 기능을 사용하기 위해 javaScriptEnabled속성의 true를 주었다. webView의 loadUrl은 해당 Url로 연결해주는 함수이다. 기본으로 정의한 URL를 연결 시켜 줬다.
## 버튼구현
홈 뒤로가기 앞으로가기 버튼은 webView자체에서 지원하기 때문에 간단한 함수만으로도 구현할 수 있었다.
homeButton.setOnClickListener {
webView.loadUrl(DEFAULT_URI)
uriEditText.setText(DEFAULT_URI)
}
forwardButton.setOnClickListener {
webView.goForward()
}
backButton.setOnClickListener {
webView.goBack()
}
# SwipeRefreshLayout으로 슬라이딩 새로고침
화면을 잡아댕겨 새로고침하는 것이 매우 어려울 줄 알았지만 제공하는 레이아웃이 있어서 활용할 수 있었다.
아래와 같이 WebView를 SwipeRefreshLayout안에다가 넣어주면 된다.
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="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>
build.gradle에 종속성을 추가해준다.
dependencies{
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
}
이후 view를 연결시켜준 후
private val refreshLayout : SwipeRefreshLayout by lazy {
findViewById(R.id.refreshLayout)
}
setOnRefreshListener에 대해서 동작을 정의해 주면 됐다.
refreshLayout.setOnRefreshListener {
webView.reload()
}
# ContentLoadingProgressBar
브라우저가 열리고 url이 바뀔때 상태에 대한 로딩바이다. 당연히 처음부터 구현할 필요는 없었고, 위에서 말했던것처럼 ChromeClient를 이용하면 된다.
webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
progressBar.progress = newProgress
}
onProgressChanged를 재정의 해주자. inner Class르 객체를 만들어서 사용해도 무관하다. 인자로 넘어오는 newProgress는 현재 프로그래스진행사항을 Int값으로 넘겨주기 때문에, 이것을 그대로 progressBar.progress에 속성값으로 사용하면 됐다.
# onBackPressed() : 핸드폰 뒤로가기 버튼 재정의
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
}else {
super.onBackPressed()
}
}
마치 LifeCycle을 이용하듯 onBackPressed을 override하여 사용하면 된다.
# EditText - imeOptions
EditText는 키패드를 통하여 사용자가 값을 입력하게 해주는 View이다 여기에는 imeOptions이라는 속성이 존재한다.
<EditText
//...
android:imeOptions="actionDone"
//...
/>
이 옵션에 빨간색 박스 속 확인버튼의 모양이 바뀐다.
그리고 setOnEditorActionListener를 이용하면 되는데, 여기서 저 버튼에 준 속성값이 actionId로 넘어오게 된다. 즉 키패드에서 확인(체크)버튼을 누르고 난 이후의 상황을 정의해줄 수 있다.
uriEditText.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val curURL = v.text.toString()
if (URLUtil.isNetworkUrl(curURL)){
webView.loadUrl(curURL)
}else {
webView.loadUrl("http://$curURL")
}
}
return@setOnEditorActionListener false
}
# 느낀점
꽤 완성도 있어 보이지만 생각보다 간단했다. 코드의 흐름을 알 수 있었고, WebView를 사용해봐서 신기했다. 꽤 많은 속성들을 재정의해줄 수 있다는 것을 알게 된거 같았고 익명객체를 내 생각대로 응용해봤는데, 상황에 맞게 사용해본거 같아서 뿌듯했다. drawable을 만들면서 디자인을 자유자재로 해볼 줄 알아야 하는거 같았다.
안드로이드 공식문서