나 만의 웹 브라우저
기능 소개
웹 서핑용으로 사용할 수 있고 나만의 몇 몇 메뉴와 컨텍스트 메뉴를 가지고 있습니다.
핵심이 되는 주제
웹 뷰 (인터넷 사용 권한 필요)
메뉴 구성
컨텍스트 메뉴 구성
암시적 인텐트 사용
실습
1) 빈 액티비티를 생성 (앱 이름 : MyWeb) & Anko 라이브러리 추가
잘 모르면 이전의 앱 제작 과정을 참고하세요~
2) 레이아웃 작업
① Plain Text 뷰 배치 (id: URLEditText , inputType: textUri, hint: http://, imeOptions: actionSearch)
(입력 자료형을 textUri로 선택하면 입력할때 소프트키보드도 URL 입력에 편리한 자판 배열로 표시됩니다)
(또 hint를 설정하면 사용자에게 웹주소를 입력해야 함을 알려줄 수 있죠)
actionSearch : 소프트키보드의 서치아이콘(돋보기 처럼 생긴...) 활성화 시켜 줍니다.
② webView 뷰 배치 (id: webView)
인터넷 권한 추가 (앱 설치시 사용자에게 권한 허용을 요청하게 됨)
-- AndroidManifest.xml
<manifest xmlns:...>
...
<uses-permission android:name="android.permission.INTERNET" />
...
</manifestxmlns>
③ 코딩 (웹뷰를 위한)
-- MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView.apply {
settings.javaScriptEnabled = true // 자바스크립트 사용 설정
webViewClient = WebViewClient() // webViewClient 객체를 생성하여 전달
// 이 행이 없으면 폰 자체 웹 브라우저가 동작함
}
webView.loadUrl("http://www.google.com") // 구글페이지 로드
URLEditText.setOnEditorActionListener { _, actionId, _ -> // 자동완성 기능으로 보면 v, actionId, event
// (반응한 뷰, 액션ID, 이벤트)
// 세 개의 인수를 사용하지만 여기선 actionId만 사용
// (사용하지 않는 인자는 _로 작성)
// 이 리스너는 editText에 글자가 입력될 때마다 호출됨
if (actionId == EditorInfo.IME_ACTION_SEARCH) { // 검색버튼이 눌렸는가?
webView.loadUrl(URLEditText.text.toString())
true // true를 반환하며 이벤트 종료
} else {
false
}
}
override fun onBackPressed() { // 액티비티에서 뒤로가기 키 이벤트 onBackPressed() 재정의(오버라이드)
if (webView.canGoBack()) {
webView.goBack() // 이전페이지로 갈 수 있으면 이전페이지로 이동하고,
} else {
super.onBackPressed() // 그렇지 않다면 본래의 동작을 수행(즉, 종료)함
}
}
}
일단 이쯤에서 실행하면 기본적인 웹 서핑이 가능합니다.
이제 옵션 메뉴와 컨텍스트 메뉴를 만들어 볼까요? 일단 메뉴는 별도의 디렉토리로 관리합니다.
File > New > Android Resource Directory 로 리소스 디렉토리 생성 (Resource type : menu)
④ 옵션 메뉴 (앱 우측 상단에 …으로 표시되는 메뉴)
i) 메뉴용 리소스 준비
생성된 Android > app > res > menu 마우스 우클릭 > Menu resource file (File name: main)
이렇게 하면 main.xml이 생성됩니다.
메뉴에 사용할 벡터 이미지 준비 (여기서는 홈 버튼에 사용할 이미지를 준비해야 하는데,
Vector Asset의 Clip Art의 검색 창에서 home으로 검색하면 곧바로 찾아줍니다~)
ii) 메뉴 구성하기
팔레트 창에서 'Menu Item'뷰를 끌어다 컴포넌트트리 창 menu 하위에 배치 (title: 검색사이트)
검색사이트 메뉴 아이템 하위에 'Menu'뷰를 끌어다 배치
Menu 뷰 하위에 세 개의 'Menu Itemp'뷰를 끌어다 배치
(id: action_daum, title: 다음)
(id: action_google, title: 구글)
(id: action_naver, title: 네이버)
같은 방법으로 검색사이트 레벨의 메뉴 아이템과 그 하위에 세 개의 메뉴 아이템들을 갖는 메뉴를
하나 더 생성 (메뉴아이템title: 개발자 정보) 하세요.
(id: action_call, title: 전화하기)
(id: action_send_text: 문자보내기)
(id: action_email: 이메일보내기)
검색사이트 레벨의 메뉴 아이템을 하나 더 추가 하세요.
(id: action_home, title: Home, icon: 위에서 만든 집 모양 벡터이미지)
툴바 밖으로 이 메뉴 아이템을 노출시키기 위해 showAsAction을 ifRoom으로 설정해 줍니다.
(아래 그림에서 보면 집 모양 아이콘이 메뉴에서 빠져나와 앱의 툴바 영역에 나타났음)
※ 참고
never
ifRoom(툴바에 여유가 있을때만)
withText(글자와 아이콘을 함께 표시)
collapseActionView (액션 뷰와 결합하면 축소되는 메뉴 생성 가능)
iii) 코딩 (메뉴를 위한)
-- MainActivity.kt
class MainActivity : AppCompatActivity() {
...
// 액티비티에서 onCreateOptionsMenu()를 오버라이드하여 메뉴 리소스 파일을 지정하면 메뉴가 표시됨
override fun onCreateOptionsMenu() : Boolean {
menuInflater.inflate(R.menu.main, menu) // MainActivity의 메뉴로 등록 (* inflate 바람넣다. 부풀리다(과장하다))
return true // 반드시 true를 반환하여 액티비티가 있음을 인식시켜줘야 함
}
// 각 옵션 메뉴 클릭 이벤트 처리기(이벤트 핸들러)
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item?.itemId) {
R.id.action_daum -> {
webView.loadUrl("http://www.daum.net")
return true // 처리를 끝낸후엔 정상종료 됐음을 알리기 위해 true를 반환
// (그리고 안드로이드에서는 자신의 작업을 하는 경우를 제외한 모든 경우에
// super 메소드를 호출하는 것이 기본규칙임
}
R.id.action_google, R.id.action_home -> { // 구글 메뉴 또는 홈버튼
webView.loadUrl("http://www.google.com")
return true
}
R.id.action_naver -> {
webView.loadUrl("http://www.naver.com")
return true
}
R.id.action_call -> {
// 전화 걸기 코드 - '암시적 인텐트'를 사용 (예제 끝 부분에 별도로 설명)
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:02-1234-5678")
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
return true
}
R.id.action_send_text -> {
// 메시지 발송 코드 (*k 마지막에 별도 정리)
return true
}
R.id.action_email -> {
// 이메일 발송 코드 (*k 마지막에 별도 정리)
return true
}
}
return super.onOptionsItemSelected(item)
}
}
⑤ 컨텍스트 메뉴 (특정 뷰를 길게 누르고 있을 때 나타나는 메뉴)
이번에는 컨텍스트 메뉴를 추가해 봅시다~
i) 컨텍스트 메뉴용 리소스 준비
Android > app > res > menu 마우스 우클릭 > Menu resource file (File name: context) --> context.xml이 생성됩니다.
ii) 메뉴 구성하기
팔레트 창에서 'Menu Item'뷰 2개를 끌어다 컴포넌트트리 창 menu 하위에 배치 (title: 검색사이트, 기본 브라우저에서 열기)
iii) 코딩 (컨텍스트 메뉴를 위한)
-- MainActivity.kt
// *1, *2 순으로 작업
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
registerForContextMenu(webView) // *2
}
override fun onCreateContextMenu( // *1
menu: ContextMenu?,
v: View?,
menuInfo: ContextMenu.ContextMenuInfo?
) {
super.onCreateContextMenu(menu, v, menuInfo)
menuInflater.inflate(R.menu.context, menu) // MainActivity의 컨텍스트 메뉴로 등록
}
// 컨텍스트 메뉴 클릭 이벤트 처리
override fun onContextItemSelected(item: MenuItem): Boolean {
when (item?.itemId) {
R.id.action_share -> {
// 페이지 공유 코드 (*k 마지막에 별도 정리)
return true
}
R.id.action_browser -> {
// 기본 웹 브라우저에서 열기 코드 (*k 마지막에 별도 정리)
return true
}
}
return super.onContextItemSelected(item)
}
}
※ 암시적 인텐트
미리 정의된 인텐트들을 말합니다.
사용 예들) 출처: https://developer.android.com/guide/components/intents-common 에서 더 많은 예를 볼 수 있어요.
그런데, Anko 라이브러리를 사용하면 아래의 사용 예들 보다 훨씬 간편하게 코딩할 수 있죠 (거의 한 줄로 처리 가능!)
(일단 암시적 인텐트 사용 예들을 볼까요)
// 전화 걸기
val intent = Intent(Intent.ACTION_DIAL) // Intent 클래스에 정의된 액션 중 전화거는 액션을 선택한 것임
intent.data = Uri.parse("tel:02-1234-5678") // "tel:"로 시작하는 Uri는 전화번호를 나타내는 국제표준임
if (intent.resolveActivity(packageManager) != null) { // intent.resolveActivity()는 인텐트를 수행하는 액티비티가 있는지를 검사
// 전화 앱이 없는 태블릿 같은 기기에서는 null값을 반환함
startActivity(intent)
}
// 문자열 보내기
val intent = Intent(Intent.ACTION_SEND)
intent.apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, "보낼 문자열")
var chooser = Intent.createChooser(intent, null)
if (intent.resolveActivity(packageManager) != null) {
startActivity(chooser)
}
}
// 웹 브라우저 띄우기
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("http://www.google.com")
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
...
...
(이번에는 Anko 라이브러리를 사용한 예 입니다)
Anko라이브러리를 사용한 예)
makeCall(전화번호) // 전화 걸기
sendSms(전화번호, [문자열]) // 문자 보내기
browse(url) // 웹 브라우저에서 열기
share(문자열, [제목]) // 문자열 공유
email(받는메일주소, [제목], [내용]) // 이메일 보내기
와! 놀랍도록 간단하군요!!
※ 상기 본 프로그램 코드에서 생략했던 코드들을 아래에 정리 했습니다. 채워 넣으세요!
주의) 특별한 경우가 아니라면 전화걸기에 암시적 인텐트를 사용하지 않을 것을 권장합니다.
별도의 권한을 필요로 하고 전화번호 입력까지만 제공하면 사용자 의지대로 전화를 걸면 되기 때문이에요.
// 메시지 발송 코드
sendSMS("02-1234-5678", webView.url + "에 들어가봐!")
// 이메일 발송 코드
email("test@daum.net", "이 쇼핑몰이 젤 좋아!", webView.url)
// 페이지 공유 코드
share(webView.url)
// 기본 웹 브라우저에서 열기 코드
browse(webView.url)
실행결과) '메뉴>검색사이트>다음'을 눌러본 상태입니다.
잘 동작하는 군요! 수고 하셨습니다~!