Words app 개요
- 시작화면에서는 알파벳 목록을 보여준다.
- 알파벳을 클릭하면 해당 알파벳으로 시작하는 단어 목록을 보여준다.
- 단어를 클릭하면 구글에 해당 단어를 검색한 창으로 연결된다.
- 알파벳 목록은 linear layout과 grid layout 두가지로 보여준다. (오른쪽 상단 아이콘을 누르면 layout이 바뀐다)
여기에서 Words 프로젝트의 틀을 다운 받는다.
starter 브랜치에서 클론하거나 클론 후 starter 브랜치에 checkout하면 된다.
git clone -b starter https://github.com/google-developer-training/android-basics-kotlin-words-app/tree/starter
- Intent 실행 시
putExtra()
로 값 전달하기- extras로 전달된 값 변수에 담기
먼저 LetterAdapter.kt
에서 버튼을 누르면 단어 목록 activity로 이동하도록 코드를 작성해야 한다. ( A 버튼을 누르면 A로 시작하는 단어 목록이 있는 액티비티로 이동 )
override fun onBindViewHolder(holder: LetterViewHolder, position: Int) {
val item = list.get(position)
holder.button.text = item.toString()
holder.button.setOnClickListener {
val context = holder.view.context
val intent = Intent(context, DetailActivity::class.java)
intent.putExtra(DetailActivity.LETTER, holder.button.text.toString())
context.startActivity(intent)
}
}
holder.button
의 onClickListener
를 설정한다.context
참조를 가져온다. Intent
를 만들어 context와 실행할 activity를 담는다.putExtra
메소드를 이용해서 인텐트로 값을 전달할 수 있다.putExtra
메서드를 호출하고, name과 버튼에 적힌 알파벳을 값으로 전달한다. startActivity
로 실행한다. DetailActivity
의 onCreate
에서 인텐트로 전달된 LETTER
값을 letterId
변수에 담는다.
val letterId = intent?.extras?.getString("letter").toString()
Bundle
유형이고 인텐트에 전달된 모든 extras에 액세스하는 방법을 제공한다.?
를 쓰는 이유DetailActivity
에서는 WordAdater
로 단어 목록을 생성한다.
WordAdapter
에 letterId
가 전달된다.
companion object
코틀린의 companion object를 사용하면 클래스의 특정 인스턴스 없이 상수를 구분하여 사용할 수 있다 (🤔)
프로그램 기간에 컴패니언 객체의 인스턴스는 하나만 존재하므로 싱글톤 패턴이라고도 한다.
정의하는 방법은 클래스랑 비슷하다.
companion object {
const val LETTER = "letter"
}
onCreate
위에 companion object를 추가하고 letterId
를 하드코딩된 "letter"
대신 상수 LETTER
를 사용하도록 수정한다.
val letterId = intent?.extras?.getString(LETTER).toString()
- 기기의 웹 브라우저 실행하기 (common 인텐트)
단어를 클릭하면 웹 브라우저를 실행해 클릭한 단어를 검색한 결과가 뜨도록 한다.
override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
val item = filteredWords[position]
val context = holder.view.context
holder.button.text = item
holder.button.setOnClickListener {
val queryUrl: Uri = Uri.parse("${DetailActivity.SEARCH_PREFIX}${item}")
val intent = Intent(Intent.ACTION_VIEW, queryUrl)
context.startActivity(intent)
}
}
filteredWords
는 List<String>
타입으로 words 리스트에서 조건에 맞는 단어들을 선택해 담은 리스트다. item
에 단어를 담는다. val words = context.resources.getStringArray(R.array.words).toList()
filteredWords = words
.filter { it.startsWith(letterId, ignoreCase = true) }
.shuffled()
.take(5)
.sorted()
WordAdapter
의 onBindViewHolder()
에서 버튼을 누르면 실행할 onClickListener
를 추가한다. queryUrl
에 SERACH_PREFIX
와 item
을 합쳐 검색할 주소 URI를 만든다.위에서 명시적 인텐트에서는 Intent(context, DetailActivity::class.java)
로 Intent를 생성했는데, 이번에는 Intent.ACTION_VIEW
와 URI
를 전달한다.
val intent = Intent(Intent.ACTION_VIEW, queryUrl)
ACTION_VIEW
는 시스템이 사용자의 웹브라우저에서 URI를 처리하는 인텐트다.
Common 인텐트 참고
CATEGORY_APP_MAPS
- 지도 앱을 실행합니다.
CATEGORY_APP_EMAIL
- 이메일 앱을 실행합니다.
CATEGORY_APP_GALLERY
- 갤러리(사진) 앱을 실행합니다.
ACTION_SET_ALARM
- 백그라운드에서 알람을 설정합니다.
ACTION_DIAL
- 전화를 겁니다.
context.startActivity(intent)
- 오른쪽 상단에 아이콘을 추가해서 알파벳 목록을 보여주는 2가지 레이아웃을 제공한다. 아이콘을 클릭해 grid layout 또는 linear layout으로 전환할 수 있다.
- 상단 앱 바를 생성한다.
vector asset을 이전에 강의에서 배운 방식으로 drawable 아래 ic_grid_layout.xml
과 ic_linear_layout.xml
을 추가한다.
res
폴더에서 New > Android Resource File
을 선택해 새 리소를 파일을 추가한다. 이때 Resource Type
은 Menu
, 파일 이름은 layout_menu
로 설정한다.
res
폴더 아래 Menu
디렉토리가 생성되었고 그 아래 layout_menu.xml
파일이 생성되었다.
<!-- layout_menu.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_switch_layout"
android:title="@string/action_switch_layout"
android:icon="@drawable/ic_linear_layout"
android:iconTint="@color/white"
app:showAsAction="always" />
</menu>
icon
: 기본값은 ic_linear_layout으로 설정되어 있다.showAsAction
: 시스템에 버튼 표시 방법을 알려준다. always로 설정되어 있으므로 이 버튼은 앱 바에 항상 표시된다.앱의 레이아웃 상태를 추적하는 속성을 만든다.
private var isLinearLayoutManager = true
사용자가 버튼을 클릭하면 레이아웃 관리자를 전환할 함수를 만든다.
기존에 onCreate
에서 LayoutManager와 adapter를 설정하는 코드는 삭제하고 그 위치에 chooseLayout()
을 호출한다.
private fun chooseLayout() {
if (isLinearLayoutManager) {
recyclerView.layoutManager = LinearLayoutManager(this)
} else {
recyclerView.layoutManager = GridLayoutManager(this, 4)
}
recyclerView.adapter = LetterAdapter()
}
GridLayoutManager
에서 4는 칼럼수다.
아이콘 변경하는 함수를 만든다.
private fun setIcon(menuItem: MenuItem?) {
if (menuItem == null)
return
menuItem.icon =
if (isLinearLayoutManager)
ContextCompat.getDrawable(this, R.drawable.ic_grid_layout)
else ContextCompat.getDrawable(this, R.drawable.ic_linear_layout)
}
메뉴를 적용하려면 메서드 2개를 재정의해야 한다. Ctrl + O
로 메서드를 찾을 수 있다.
onCreateOptionsMenu
: 메뉴(앱 바)를 inflate 한다.
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.layout_menu, menu)
val layoutButton = menu?.findItem(R.id.action_switch_layout)
setIcon(layoutButton)
return true
}
onOptionsItemSelected
: 버튼이 선택될 때 실제로 chooseLayout()
을 호출한다.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_switch_layout -> {
isLinearLayoutManager = !isLinearLayoutManager
chooseLayout()
setIcon(item)
return true
}
else -> super.onOptionsItemSelected(item)
}
}
이제 모든 기능이 완성되었다! 아이콘 클릭으로 레이아웃 변경도 되고, A버튼을 클릭하면 A로 시작하는 단어 목록이 나오고, 단어를 클릭하면 웹 브라우저에 검색한 결과가 나온다.