[Live Coding] (1/23 토) 
Fast Campus 안드로이드 앱 개발 입문 4기
Live Coding - Camp_WebBrowser
작성자: 하동욱 (​https://fb.com/mindwing​)
이 강의노트는 ​https://goo.gl/rxb2JF​ 에서 보실 수 있습니다.
 
목차 
0) 들어가기에 앞서 
1) Android Studio 기동해서 새 프로젝트 만들기 
2) 실행해보기 
3) Action Bar 숨기기 
4) 기본 UI 만들고 이벤트 핸들링 코딩해보기 
5) WebView 를 써보자 
6) Back, Forward 버튼 붙이기 
7) Design Support Library 를 이용하여 Snackbar 써보기 
8) github 에서 프로젝트를 clone 하기 
 
● 0) 들어가기에 앞서 
○ Live Coding 은 처음부터 앱의 완성까지 직접 코드를 손수 짜보는 과정입니다. 
○ Android SDK 는 23 버전을 사용하므로, 반드시 버전을 확인하시기 바랍니다. 
 
   
○ File > Settings... > Appearance & Behavior > System Settings > Android SDK 
의 SDK Platforms 탭에서 Android 6.0 (Marshmallow) 가 설치되어 있어야 
합니다. (API 23) 
 
 
○ File > Settings... > Appearance & Behavior > System Settings > Android SDK 
의 SDK Tools 탭에서 Android Support Library 와 Android Support Repository 
가 설치되어 있어야 합니다. 
 
 
● 1) Android Studio 기동해서 새 프로젝트 만들기 
○ 다음과 같이 Android 초기메뉴에서 새 프로젝트를 만들기 메뉴를 선택합니다. 
■ 혹은, 이미 Android Studio 를 기동중이라면 File > New > New 
Project... 메뉴를 선택합니다. 
 
 
 
 
 
 
○ 다음과 같이 Application 에 WebBrowser 를 입력하고, Company Name 에는 
여러분들의 적절한 도메인을 적어주면 됩니다. 
■ 저는 mindwing.kr 로 적었습니다. 도메인 이름과 Application 이름은 
합쳐져서 Package name 으로 표현되는데, 이 이름은 Google Play 
스토어내에서 이 앱을 유일하게 구별하는 이름표 역할을 하므로, 
스토어에 올릴 앱을 만들 때에는 신중하게 결정해야 합니다. 
 
 
 
○ 이제 다음 단계는 지원단말을 결정하는 메뉴입니다. Google 권장사항인 API 
15 를 최저지원 API 로 삼으면 Google Play 스토어에 접속하는전세계의 
Android 단말중에서 96.2% 의 이용자들을 대상으로 할 수 있으므로, 그대로 
따릅니다. 
■ 나머지 3.8% 의 단말은 API 14 이하인 단말들입니다. 이 단말들은 
사용률이 너무 낮고 단말자체도 사용자가 잘 사용하지 않을 가능성이 
높습니다. 이런 단말들은 과감하게 지원하지 않도록 하는 것이 
문제발생소지를 없애는 길입니다. 
 
 
 
○ 이제 Activity 를 자동으로 만들어주길 원하는지 물어보는 화면입니다. 빈 
Activity 가 하나 있으면 되므로, Empty Activity 를 선택합니다. 
■ Blank Activity 는 Floating Action Button 이 추가되므로 필요한 
경우에만 사용합니다. 
■ 수동으로 Activity 를 추가하려 한다면 "Add No Activity” 를 선택합니다. 
 
 
 
 
○ 마지막으로 Activity 의 이름과 layout xml 파일 이름을 정하는 화면이며, 
Android 가 제시하는 기본값을 그대로 사용합니다. 
 
 
 
○ 프로젝트가 막 생성되었을 때에는 빌드와 내부 인덱싱 작업도 시작됩니다. 이 
과정은 부하가 꽤 걸리기 때문에, 특히 램이 부족하거나 SSD 가 아닌 HDD 를 
장착한 경우에는 잠시동안 아무 작업도 하지 말고 가만히 내버려두시기 
바랍니다. 
■ 빌드가 완료되어야 리소스 관련 클래스인 R 을 사용할 수 있게 됩니다. 
■ 모든 백그라운드 작업이 끝나고 빨간색 에러표시도 없이 깨끗한 상태가 
될 때까지 기다려주세요. 
 
 
 
 
○ 위 이미지와 같이 되셨나요? 
 
 
 
 
○ 이렇게 되셨다면 위 이미지와 같이 XML 아이콘을 누른 다음 activity_main.xml 
파일을 선택해서 열어보겠습니다. 
 
 
 
 
 
○ 만약, activity_main.xml 파일을 열었을 때에 위 이미지처럼 Rendering Problem 
이 발생한다면 API 23 대신 API 22 를 선택했다가 다시 API 23 을 
선택해주세요. 그래도 문제가 있다면 화살표 모양의 동그라미 refresh 버튼을 
눌러주세요. 
■ 문제가 해결되지 않는다면 말씀해주세요. 개별적으로 
수정해드리겠습니다. 
■ 이 문제는 Android Studio 와 Gradle 빌드시스템이 서로 독립적으로 
실행되다가 서로 약간 동기화가 되지 못하는 버그때문입니다. 
그러므로, 프로젝트를 새로 생성하는 등의 Android Studio 가 바쁘게 
움직일 때에는 별도의 작업을 시키지 말고 가만 놔두시는 것이 
좋습니다. (안정화버전이 나오리라 기대합니다.) 
 
 
 
○ 문제가 해결되었습니다. (문제가 해결되지 않은 분들은 제가 개별적으로 
해결해드리겠습니다.) 
 
○ 이제 코딩타임입니다. 
   
○ 2) 실행해보기 
○ Android Studio 가 마련해준 프로젝트 형상을 그대로 실행해봅니다. 
■ 초록색 화살표 아이콘을 누르면 앱이 실행됩니다. 
 
 
 
○ 이 문서에서는 에뮬레이터에서 개발해보겠지만, 개발하려는 앱의 덩치가 
크거나, 단말이 존재하지 않는 특정 API level 을 테스트해야 하거나, 램이 
넉넉하지 않은 개발환경에서는 에뮬레이터를 쓰는 것이 무척 힘든 일이므로, 
직접 단말기를 연결해서 개발하는 것을 권장합니다. 
■ i7 쿼드코어에 램 8GB 라면 도전해보세요. 
 
 
 
○ 다음은 WebBrowser 앱이 실행되어서 MainActivity 가 보여지는 모습입니다. 
   
○ 3) Action Bar 숨기기 
○ Android Studio 가 기본으로 만들어준 UI 에는 ActionBar 가 포함되어 
있습니다. 
○ 이 예제에서는 ActionBar 를 쓰지 않을 것이므로, 이를 숨기겠습니다. 
■ ActionBar 는 테마에 속하는 부분이므로 테마를 수정해야 합니다. 
○ 다음 이미지처럼 app > res > values > styles.xml 파일을 엽니다. 
 
 
 
○ 위 이미지처럼 4번째 줄에 "Theme.AppCompat.Light.” 이라고 되어 있는 부분 
뒤에 있는 “DarkActionBar” 를 지우고 점이 있는 부분에 커서를 위치시킨 다음 
ctrl ­ space 키를 누릅니다. 
■ "Theme.AppCompat.Light.NoActionBar" 를 선택합니다. 
● NoActionBar 테마를 이용하도록 수정하는 것입니다. 
 
[res/values/styles.xml] 
<resources> 
 
   ​<!­­ Base application theme. ­­> 
   ​<style ​name=​"AppTheme" ​parent=​"​Theme.AppCompat.Light.NoActionBa​r​"​> 
       ​<!­­ Customize your theme here. ­­> 
       ​<item ​name=​"colorPrimary"​>​@color/colorPrimary​</item> 
       <item ​name=​"colorPrimaryDark"​>​@color/colorPrimaryDark​</item> 
       <item ​name=​"colorAccent"​>​@color/colorAccent​</item> 
   </style> 
 
</resources> 
 
 
○ 이제 다음 이미지처럼 NoActionBar 로 변경되었습니다. 
○ Android Studio 2.0 을 실행중이라면 코드를 수정 후 실행아이콘에 번개모양이 
나오는 것을 볼 수 있습니다. 
■ Instant Run 이라는 기능으로 코드를 수정한 다음 apk 를 다시 만들어서 
단말이나 에뮬레이터에 인스톨하고 다시 앱을 실행하는 복잡한 과정을 
거치지 않고, 현재 실행되어 있는 앱에서 변경된Activity 만 바꿔주는 
기능입니다. 
■ 개발속도를 빠르게 가져갈 수 있는 Android Studio 2.0 의 새로운 
기능입니다. 
 
 
 
 
○ 다음 그림은 Action Bar 가 사라진 모습입니다. 
■ Instant Run 이 적용되었을 경우 안내 Toast 가 떠서 이를 알 수 있게 
해줍니다. 
 
 
 
 
○ 참고로, 테마를 바꾸지 않고 직접 코드상에서 Action Bar 를 없앨 수도 
있습니다. 
■ 다음처럼 hideActionBar() 메서드를 작성합니다. 
■ onCreate() 메서드에서 hideActionBar() 메서드를  호출해서 앱이 
기동되자마자 ActionBar 를 숨길 수 있도록 합니다. 
○ 이 예제는 ​SDK 23​ 버전을 사용하므로, 이전 버전으로 작성하면 
hideActionBar() 메서드에 문제가 발생할 수 있습니다. 
 
[MainActivity.java] 
... 
import ​android.support.v7.app.ActionBar​; 
... 
 
public class ​MainActivity ​extends ​AppCompatActivity { 
 
    @Override 
    ​protected void ​onCreate​(Bundle savedInstanceState) { 
     ​   ​super​.onCreate(savedInstanceState)​; 
     ​   ​setContentView(R.layout.​activity_main​)​; 
 
        hideActionBar()​; 
    ​} 
 
    ​private void ​hideActionBar​() { 
   ​    ​ ​ActionBar actionBar = getSupportActionBar()​; 
    ​    ​actionBar.hide()​; 
    } 
} 
 
 
○ 이번 강의에서는 코드에서 변경하는 방법 대신 테마를 변경하는 방법을 
이용하니, 이 방법은 참고로만 알아두세요. 
 
   
○ 4) 기본 UI 만들고 이벤트 핸들링 코딩해보기  
○ 웹브라우저에는 기본적으로 URL 을 입력받는 창과 웹화면이 보이는 부분이 
있습니다. 
■ URL 을 입력받기 위해서는 EditText 컴포넌트를 이용하며, 웹화면을 
보여주기 위해 WebView 컴포넌트를 이용합니다. 
■ 웹페이지의 실질적인 처리는 모두 WebView 컴포넌트가 담당하므로, 
웹브라우저 앱을 만들기 위해서는 WebView 컴포넌트만 잘 다룰 수 
있으면 됩니다. 
○ 아래 코드는 MainActivity 에서 사용하는 UI 레이아웃입니다. 
■ <RelativeLayout> 으로 전체를 레이아웃합니다. 
■ 그 내부에 <EditText> 와 <WebView> 가 포함됩니다. 
■ 각각의 ID는 ​text_url​ 과 ​webview​ 이며, 이는 Java 코드에서 사용할 
이름이 됩니다. 
 
[res/layout/activity_main.xml] 
<?​xml version=​"1.0" ​encoding=​"utf­8"​?> 
<RelativeLayout 
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" 
   ​android​:paddingBottom=​"@dimen/activity_vertical_margin" 
   ​android​:paddingLeft=​"@dimen/activity_horizontal_margin" 
   ​android​:paddingRight=​"@dimen/activity_horizontal_margin" 
   ​android​:paddingTop=​"@dimen/activity_vertical_margin" 
   ​tools​:context=​"kr.mindwing.webbrowser.MainActivity"​> 
 
   <EditText 
       ​android​:id=​"@+id/​text_url​" 
       ​android​:layout_width=​"match_parent" 
       ​android​:layout_height=​"wrap_content" 
       ​android​:singleLine=​"true" ​/> 
 
   <WebView 
       ​android​:id=​"@+id/​webview​" 
       ​android​:layout_width=​"match_parent" 
       ​android​:layout_height=​"match_parent" 
       ​android​:layout_below=​"@id/text_url" /​> 
 
</RelativeLayout> 
 
 
○ 이제 URL 을 입력받아서 Toast 로 띄워보는 Java 코드를 짜보겠습니다. 
■ 기본적인 이벤트 핸들링을 하는 방법을 통해서 Toast 를 띄웁니다. 
package ​kr.mindwing.webbrowser​; 
 
import ​android.os.Bundle​; 
import ​android.support.v7.app.AppCompatActivity​; 
import ​android.view.KeyEvent​; 
import ​android.view.View​; 
import ​android.widget.EditText​; 
import ​android.widget.Toast​; 
 
public class ​MainActivity ​extends ​AppCompatActivity { 
 
   ​private ​EditText ​textUrl​; 
   private ​View.OnKeyListener ​listener ​= ​new ​View.OnKeyListener() { 
 
       ​@Override 
       ​public boolean ​onKey​(View v​, int ​keyCode​, ​KeyEvent event) { 
           ​if ​(event.getAction() != KeyEvent.​ACTION_DOWN​) { 
               ​return false; 
           ​} 
 
           ​boolean ​processed = ​false; 
 
           switch ​(keyCode) { 
               ​case ​KeyEvent.​KEYCODE_ENTER​: 
                   Toast.​makeText​( 
                           MainActivity.​this, 
                           ​textUrl​.getText().toString()​, ​Toast.​LENGTH_SHORT​).show()​; 
 
                   ​processed = ​true; 
                   break; 
 
               default​: 
                   ​break; 
           ​} 
 
           ​return ​processed​; 
       ​} 
   }​; 
 
   ​@Override 
   ​protected void ​onCreate​(Bundle savedInstanceState) { 
       ​super​.onCreate(savedInstanceState)​; 
       ​setContentView(R.layout.​activity_main​)​; 
       ​setupUI()​; 
   ​} 
 
   ​private void ​setupUI​() { 
       ​textUrl ​= (EditText) findViewById(R.id.​text_url​)​; 
       ​textUrl​.setOnKeyListener(​listener​)​; 
   ​} 
} 
 
 
○ TextView 영역에 “camp android” 라고 입력하고서 엔터키를 누르면, 이벤트가 
Java 코드의 ​OnKeyListener​ 객체에게 전달되어 Toast 를 띄우는 코드를 
실행하게 됩니다. 
 
○ 5) WebView 를 써보자 
○ android.webkit.WebView 클래스는 웹페이지를 다루는 기능을 가지고 
있습니다. 
■ 웹페이지의 URL 만 지정해주면 알아서 웹서버에 접속해서 필요한 
파일들을 다운로드받아서 표시해줍니다. 
○ 문자열을 입력받아서 Toast 를 띄웠던 이전 코드를 수정해서 웹페이지의 URL 
을 입력받은 다음, 이를 WebView 객체에게 전달해보겠습니다. 
■ XML 로 된 UI 코드에서 이미 TextView 와 더불어 WebView 객체도 
생성되도록 지정되어 있습니다. 
 
[MainActivity.java] 
package ​kr.mindwing.webbrowser​; 
 
import ​android.os.Bundle​; 
import ​android.support.v7.app.AppCompatActivity​; 
import ​android.view.KeyEvent​; 
import ​android.view.View​; 
import ​android.webkit.WebView​; 
import ​android.widget.EditText​; 
 
public class ​MainActivity ​extends ​AppCompatActivity { 
 
   ​private ​EditText ​textUrl​; 
   private ​WebView ​webView​; 
   private ​View.OnKeyListener ​listener ​= ​new ​View.OnKeyListener() { 
 
       ​@Override 
       ​public boolean ​onKey​(View v​, int ​keyCode​, ​KeyEvent event) { 
           ​if ​(event.getAction() != KeyEvent.​ACTION_DOWN​) { 
               ​return false; 
           ​} 
 
           ​boolean ​processed = ​false; 
 
           switch ​(keyCode) { 
               ​case ​KeyEvent.​KEYCODE_ENTER​: 
                   ​webView​.loadUrl(​textUrl​.getText().toString())​; 
 
                   ​processed = ​true; 
                   break; 
 
               default​: 
                   ​break; 
           ​} 
 
           ​return ​processed​; 
       ​} 
   }​; 
 
   ​@Override 
   ​protected void ​onCreate​(Bundle savedInstanceState) { 
       ​super​.onCreate(savedInstanceState)​; 
       ​setContentView(R.layout.​activity_main​)​; 
       ​setupUI()​; 
   ​} 
 
   ​private void ​setupUI​() { 
       ​textUrl ​= (EditText) findViewById(R.id.​text_url​)​; 
       ​textUrl​.setOnKeyListener(​listener​)​; 
       ​textUrl​.setText(​"http://facebook.com"​)​; 
 
       ​webView ​= (WebView) findViewById(R.id.​webview​)​; 
   ​} 
} 
 
 
○ TextView 에 기본값으로 “​http://facebook.com​” 을 설정했습니다. 
■ 엔터키만 누르면 바로 페이스북으로 접속할 수 있습니다. 
○ 그런데, 다음 화면과 같은 에러 화면이 떴습니다. 
 
 
 
 
 
○ "android webview ERR_CACHE_MISS” 키워드로 구글링하면 매우 많은 
검색결과가 나오는데, 어느 링크를 눌러봐도 INTERNET 퍼미션이 없어서 
발생하는 문제라는 설명을 하고 있습니다. 
 
 
 
 
○ Internet 퍼미션은 AndroidManifest.xml 에 다음과 같이 적어주면 됩니다. 
■ 퍼미션을 일일이 적어주게 하는 이유는 안드로이드 시스템이 어떤 앱이 
어떤 퍼미션을 이용하는지 내부적으로 추적하고 있어야 하기 
때문입니다. 건물 등기부등본에 시시콜콜한 내용들을 모두 적어두게 
하고 이를 법원이 관리하는 것과 마찬가지입니다. 
 
[app/manifests/AndroidManifest.xml] 
<?​xml version=​"1.0" ​encoding=​"utf­8"​?> 
<manifest ​xmlns:​android​=​"http://schemas.android.com/apk/res/android" 
   ​package=​"kr.mindwing.webbrowser"​> 
 
  ​ <uses­permission ​android​:name=​"android.permission.INTERNET" ​/> 
 
   <application 
       ​android​:allowBackup=​"true" 
       ​android​:icon=​"@mipmap/ic_launcher" 
       ​android​:label=​"@string/app_name" 
       ​android​:supportsRtl=​"true" 
       ​android​:theme=​"@style/AppTheme"​> 
       <activity ​android​:name=​".MainActivity"​> 
           <intent­filter> 
               <action ​android​:name=​"android.intent.action.MAIN" ​/> 
 
               <category ​android​:name=​"android.intent.category.LAUNCHER" ​/> 
           </intent­filter> 
       </activity> 
   </application> 
 
</manifest> 
 
 
○ 이제 문제가 고쳐졌는지 확인해보겠습니다. 
■ 로그인화면이 나오긴 나왔는데, 이것은 지금 만든 WebBrowser 앱이 
아니고, 안드로이드 기본 내장 웹브라우저가 대신 실행된 모습입니다. 
 
 
 
○ 이것은 WebView 가 Facebook 의 redirect 를 처리할 능력이 없어서, 대신 다른 
웹브라우저앱을 띄웠기 때문입니다. 
■ 이 문제를 해결하기 위해서는 WebView 가 기본적인 기능을 처리할 수 
있도록 WebViewClient 객체를 만들어 지정하면 됩니다. 
■ WebViewClient 는 개발자가 추가코드를 적용하여 기능을 확장해서 쓸 
수 있도록 해주기 위한 장치입니다. 
 
[MainActivity.java] 
import ​android.webkit.WebView​; 
import ​android.webkit.WebViewClient​; 
import ​android.widget.EditText​; 
 
... 
    private void ​setupUI​() { 
        ​textUrl ​= (EditText) findViewById(R.id.​text_url​)​; 
        ​textUrl​.setOnKeyListener(​listener​)​; 
        ​textUrl​.setText(​"http://facebook.com"​)​; 
 
        ​webView ​= (WebView) findViewById(R.id.​webview​)​; 
        ​webView.setWebViewClient(​new ​WebViewClient())​; 
    } 
... 
 
 
○ 다시 앱을 실행해보겠습니다. 
■ 이제는 로그인 화면이 WebView 내에서 잘 나오는 것을 볼 수 
있습니다. 
 
   
○ 6) Back, Forward 버튼 붙이기 
○ 이제 기본적인 동작은 하는 것 같은데, 일반적인 웹브라우저와 비교하면 뒤로 
가기와 앞으로 가기 버튼이 없는 것이 눈에 띕니다. 
■ 버튼 2개를 만들고, 이에 대한 이벤트 처리 코드를 작성해보겠습니다. 
 
[activity_main.xml] 
<?​xml version=​"1.0" ​encoding=​"utf­8"​?> 
<RelativeLayout 
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" 
   ​android​:paddingBottom=​"@dimen/activity_vertical_margin" 
   ​android​:paddingLeft=​"@dimen/activity_horizontal_margin" 
   ​android​:paddingRight=​"@dimen/activity_horizontal_margin" 
   ​android​:paddingTop=​"@dimen/activity_vertical_margin" 
   ​tools​:context=​"kr.mindwing.webbrowser.MainActivity"​> 
 
   <LinearLayout 
       ​android​:id=​"@+id/upper" 
       ​android​:layout_width=​"match_parent" 
       ​android​:layout_height=​"wrap_content" 
       ​android​:orientation=​"horizontal" 
       ​android​:padding=​"1dp"​> 
 
       <EditText 
           ​android​:id=​"@+id/text_url" 
           ​android​:layout_width=​"0dp" 
           ​android​:layout_height=​"wrap_content" 
           ​android​:layout_weight=​"6" 
           ​android​:singleLine=​"true" ​/> 
 
       <Button 
           ​android​:id=​"@+id/back" 
           ​android​:layout_width=​"0dp" 
           ​android​:layout_height=​"wrap_content" 
           ​android​:layout_weight=​"2" 
           ​android​:text=​"뒤로" ​/> 
 
       <Button 
           ​android​:id=​"@+id/forward" 
           ​android​:layout_width=​"0dp" 
           ​android​:layout_height=​"wrap_content" 
           ​android​:layout_weight=​"3" 
           ​android​:onClick=​"goForward" 
           ​android​:text=​"앞으로" ​/> 
   </LinearLayout> 
   
   <WebView 
       ​android​:id=​"@+id/webview" 
       ​android​:layout_width=​"match_parent" 
       ​android​:layout_height=​"match_parent" 
       ​android​:layout_below=​"@id/​upper​" ​/> 
 
</RelativeLayout> 
 
 
[MainActivity.java] 
package ​kr.mindwing.webbrowser​; 
 
import ​android.os.Bundle​; 
import ​android.support.v7.app.AppCompatActivity​; 
import ​android.view.KeyEvent​; 
import ​android.view.View​; 
import ​android.webkit.WebView​; 
import ​android.webkit.WebViewClient​; 
import ​android.widget.Button​; 
import ​android.widget.EditText​; 
import ​android.widget.Toast​; 
 
public class ​MainActivity ​extends ​AppCompatActivity { 
 
   ​private ​EditText textUrl​; 
   private ​WebView webView​; 
   private ​Button backButton​, ​forwardButton​; 
   private ​View.OnKeyListener listener = ​new ​View.OnKeyListener() { 
 
       @Override 
       ​public boolean ​onKey(View v​, int ​keyCode​, ​KeyEvent event) { 
           ​if ​(event.getAction() != KeyEvent.ACTION_DOWN) { 
               ​return false; 
           ​} 
 
           ​boolean ​processed = ​false; 
 
           switch ​(keyCode) { 
               ​case ​KeyEvent.KEYCODE_ENTER: 
                   webView.loadUrl(textUrl.getText().toString())​; 
 
                   ​processed = ​true; 
                   break; 
 
               default​: 
                   ​break; 
           ​} 
 
           ​return ​processed​; 
       ​} 
   }​; 
 
   ​@Override 
   ​protected void ​onCreate(Bundle savedInstanceState) { 
       ​super​.onCreate(savedInstanceState)​; 
       ​setContentView(R.layout.activity_main)​; 
       ​setupUI()​; 
   ​} 
 
   ​private void ​setupUI​() { 
       ​backButton ​= (Button) findViewById(R.id.​back​)​; 
       ​backButton​.setOnClickListener(​new ​View.OnClickListener() { 
 
           ​@Override 
           ​public void ​onClick​(View v) { 
               ​if ​(​webView​.canGoBack()) { 
                   ​webView​.goBack()​; 
               ​} ​else ​{ 
                   Toast.makeText(MainActivity.​this, ​"맨 뒷페이지 입니다."​, 
Toast.​LENGTH_SHORT​).show()​; 
               ​} 
           } 
       })​; 
 
       ​forwardButton ​= (Button) findViewById(R.id.​forward​)​; 
 
       ​textUrl ​= (EditText) findViewById(R.id.​text_url​)​; 
       ​textUrl​.setOnKeyListener(​listener​)​; 
       ​textUrl​.setText(​"http://google.com"​)​; 
 
       ​webView ​= (WebView) findViewById(R.id.​webview​)​; 
       ​webView​.setWebViewClient(​new ​WebViewClient())​; 
   ​} 
 
   ​public void ​goForward(View ​view​) { 
       ​if ​(webView.canGoForward()) { 
           webView.goForward()​; 
       ​} ​else ​{ 
           Toast.makeText(MainActivity.​this, ​"맨 앞페이지 입니다."​, 
Toast.LENGTH_SHORT) 
                   .show()​; 
       ​} 
   } 
} 
 
 
○ 뒤로 가기 버튼은 ​setOnClickListener()​ 메서드를 이용해서 OnClickListener 
객체를 통해 이벤트 핸들링을 합니다. 
○ 반면, 앞으로 가기 버튼은 xml 에서 ​onClick​ 속성을 이용해서 이벤트 핸들링에 
사용할 메서드를 지정합니다. 
■ 인자로 전달되는 View 객체는 앞으로 가기 버튼입니다. 
■ Button forwardButton = (Button) view; 
○ 이제 실행을 하면 다음 화면과 같은 모양이 됩니다. 
■ 더 이상 앞으로 갈 수 없거나 뒤로 갈 수 없을 때에는 안내 Toast 가 
뜹니다. 
 
 
 
 
○ 버튼을 보면 WebView 영역과 별다른 구분선이 없습니다. 
■ 이럴 때에는 <shape> 태그를 이용해서 구분을 지어주고 싶은 UI 
컴포넌트들에게 UI효과를 주면 됩니다. 
○ 다음과 같이 shape.xml 파일을 만들어주세요. 
 
 
 
 
 
 
○ shape.xml 파일이 만들어졌으면 다음 내용을 코딩해주세요. 
 
[res/drawable/shape.xml] 
<?​xml version=​"1.0" ​encoding=​"utf­8"​?> 
<shape ​xmlns:​android​=​"http://schemas.android.com/apk/res/android"​> 
 
   <solid ​android​:color=​"#ffffaf" ​/> 
 
   <stroke 
       ​android​:width=​"2px" 
       ​android​:color=​"#ff7f00" ​/> 
 
   <corners ​android​:radius=​"10dp" ​/> 
 
</shape> 
 
 
○ 다음과 같이 LinearLayout 에 shape 속성을 부여합니다. 
 
[activity_main.xml] 
... 
 
<LinearLayout 
   ​android​:id=​"@+id/upper" 
   ​android​:layout_width=​"match_parent" 
   ​android​:layout_height=​"wrap_content" 
   ​android​:background=​"@drawable/shape" 
   ​android​:orientation=​"horizontal" 
   ​android​:padding=​"1dp"​> 
 
... 
 
 
○ 이제 URL 입력창과 버튼들을 담는 Linear Layout 영역이 별도의 UI 효과를 
가지는 것을 볼 수 있습니다. 
 
 
 
○ Enter 키를 누르면 google.com 에 바로 접속해서 검색결과를 보거나 여러 
페이지간에 뒤로 가기와 앞으로 가기를 할 수 있습니다. 
 
 
 
 
 
   
○ 7) Design Support Library 를 이용하여 Snackbar 
써보기 
○ Android 는 API level 에 따라 추가기능을 제공하고 있습니다. 
■ LolliPop (API 21, 22) 보다 Marshmallow (API 23) 가 더 많은 기능을 
제공합니다. 
■ 따라서, API 23 에서 추가된 기능은 API 21 만 제공하는 안드로이드 
단말에서는 제공되지 않습니다. 
■ 단말제조사가 Marshmallow 업그레이드 서비스를 제공해주어야 
추가기능을 써볼 수 있습니다. 
○ 이러한 불편함을 해소하기 위해 API level 이 낮더라도 기존 기능만을 이용하여 
새로운 기능을 추가 구현하고, 이를 라이브러리형태로 배포해줍니다. 이런 
추가라이브러리를 support library 라고 합니다. 
■ 라이브러리 이름은 다음과 같이 생겼습니다. 
● com.android.support:appcompat­v7 
● com.android.support:design 
■ v7 이라고 버전숫자가 붙은 라이브러리는 최소한 API level 7 이상의 
단말이면 쓸 수 있다는 뜻입니다. 
■ 버전숫자가 없는 라이브러리는 특별히 제한되는 API level 은 없지만, 
Design Support Library 는 com.android.support:appcompat­v7 
라이브러리에 의존적이기 때문에 결론적으로 최소한 API level 7 
이어야 합니다. 
○ 이 강의에서도 이미 AppCompat Support Library 를 사용하고 있는데, Main 
Activity 가 상속하고 있는 AppCompatActivity 가 바로 그것입니다. 
■ 전체 이름은 android.support.v7.app.AppCompatActivity 입니다. 
 
 
import ​android.support.v7.app.AppCompatActivity​; 
 
..... 
 
public class ​MainActivity ​extends ​AppCompatActivity​ { 
 
 
○ 이렇게 Support Library 를 사용하고자 한다면 다음과 같이 build.gradle 파일에 
내용이 추가되어야 합니다. 
■ 빌드시스템에게 어떤 라이브러리를 쓸 것인지 알려주는 것입니다. 
 
 
[Gradle Scripts/build.gradle (Module: app)] 
apply ​plugin​: ​'com.android.application' 
 
android { 
   compileSdkVersion ​'Google Apis:Google Apis:23' 
   ​buildToolsVersion ​"23.0.2" 
 
   ​defaultConfig { 
       ​applicationId ​"kr.mindwing.webbrowser" 
       ​minSdkVersion ​15 
       ​targetSdkVersion ​23 
       ​versionCode ​1 
       ​versionName ​"1.0" 
   ​} 
   buildTypes { 
       release { 
           ​minifyEnabled ​false 
           ​proguardFiles ​getDefaultProguardFile​(​'proguard­android.txt'​), 
'proguard­rules.pro' 
       ​} 
   } 
} 
 
dependencies { 
   compile fileTree(​dir​: ​'libs'​, ​include​: [​'*.jar'​]) 
   testCompile ​'junit:junit:4.12' 
   ​compile ​'com.android.support:appcompat­v7:23.+' 
   ​compile ​'com.android.support:design:23.+' 
} 
 
 
○ 이 강의에서 추가로 쓰고자 하는 Support Library 는 design 입니다. 
○ 여기에서 제공하는 기능중 Snackbar 를 쓸 것입니다. 
■ Snackbar 는 Toast 와 유사한 기능을 제공하지만, Toast 는 앱의 
화면과 상관없이 그 위에 토스트처럼 툭~ 튀어나오는 기능이라면, 
Snackbar 의 경우 특정 View 를 기준으로 숨겨진 화면처럼 존재하며 
필요할 때마다 살짝 튀어나와서 내용을 보여줍니다. 
○ 다음과 같이 MainActivity 에서 Toast 를 띄우는 부분에 Snackbar 도 같이 
띄워보도록 코딩합니다. 
 
[MainActivity.java] 
package ​kr.mindwing.webbrowser​; 
 
import ​android.os.Bundle​; 
import ​android.support.design.widget.Snackbar​; 
import ​android.support.v7.app.AppCompatActivity​; 
import ​android.view.KeyEvent​; 
import ​android.view.View​; 
import ​android.webkit.WebView​; 
import ​android.webkit.WebViewClient​; 
import ​android.widget.Button​; 
import ​android.widget.EditText​; 
import ​android.widget.Toast​; 
 
public class ​MainActivity ​extends ​AppCompatActivity { 
 
   ​private ​EditText ​textUrl​; 
   private ​WebView ​webView​; 
   private ​Button ​backButton​, ​forwardButton​; 
   private ​View.OnKeyListener ​listener ​= ​new ​View.OnKeyListener() { 
 
       ​@Override 
       ​public boolean ​onKey​(View v​, int ​keyCode​, ​KeyEvent event) { 
           ​if ​(event.getAction() != KeyEvent.​ACTION_DOWN​) { 
               ​return false; 
           ​} 
 
           ​boolean ​processed = ​false; 
 
           switch ​(keyCode) { 
               ​case ​KeyEvent.​KEYCODE_ENTER​: 
                   ​webView​.loadUrl(​textUrl​.getText().toString())​; 
 
                   ​processed = ​true; 
                   break; 
 
               default​: 
                   ​break; 
           ​} 
 
           ​return ​processed​; 
       ​} 
   }​; 
 
   ​@Override 
   ​protected void ​onCreate​(Bundle savedInstanceState) { 
       ​super​.onCreate(savedInstanceState)​; 
       ​setContentView(R.layout.​activity_main​)​; 
       ​setupUI()​; 
   ​} 
 
   ​private void ​setupUI​() { 
       ​backButton ​= (Button) findViewById(R.id.​back​)​; 
       ​backButton​.setOnClickListener(​new ​View.OnClickListener() { 
 
           ​@Override 
           ​public void ​onClick​(View v) { 
               ​if ​(​webView​.canGoBack()) { 
                   ​webView​.goBack()​; 
               ​} ​else ​{ 
                   noti(​"맨 뒷페이지 입니다."​)​; 
               ​} 
           } 
       })​; 
 
       ​forwardButton ​= (Button) findViewById(R.id.​forward​)​; 
 
       ​textUrl ​= (EditText) findViewById(R.id.​text_url​)​; 
       ​textUrl​.setOnKeyListener(​listener​)​; 
       ​textUrl​.setText(​"http://google.com"​)​; 
 
       ​webView ​= (WebView) findViewById(R.id.​webview​)​; 
       ​webView​.setWebViewClient(​new ​WebViewClient())​; 
   ​} 
 
   ​public void ​goForward​(View view) { 
       ​if ​(​webView​.canGoForward()) { 
           ​webView​.goForward()​; 
       ​} ​else ​{ 
           noti(​"맨 앞페이지 입니다."​)​; 
       ​} 
   } 
 
   ​private void ​noti​(String str) { 
       Toast.​makeText​(​this, ​str​, ​Toast.​LENGTH_SHORT​).show()​; 
       ​Snackbar.​make​(​webView​, ​str​, ​Snackbar.​LENGTH_SHORT​).show()​; 
   ​} 
} 
 
○ noti() 메서드가 추가되었습니다. 
■ 이 메서드는 기존에 Toast 를 띄우던 코드를 대체해서 호출하도록 되어 
있으며, 이 메서드 안에서는 Toast 도 띄우고 Snackbar 도 띄웁니다. 
■ 두 개의 UI 가 어떻게 다른지 비교해보세요. 
 
 
   
○ 8) github 에서 프로젝트를 clone 하기 
○ 지금 작성된 코드는 다음 URL 에서 볼 수 있습니다. 
■ https://github.com/mindwing/Camp_WebBrowser 
○ Android Studio 에서 바로 clone 해서 빌드 후 안드로이드폰에 올려볼 수 
있습니다. 
○ Android Studio 2.0 을 쓰지 않는다면 clone 이후 에러가 발생할 수 있습니다. 
문제가 발생하신 분은 제가 직접 빌드환경을 수정해드리겠습니다. 
 
 
 
 
수고하셨습니다~ 
 
복습 꼭 하시고요. 
내용 분석해보시다가 궁금하신 
내용은 언제든지 Slack 에 
질문올려주세요. 
 

[Live coding 1-23 토] camp-web_browser