1. 시작하기 전에
이 Codelab에서는 기존 Dice Roller Android 앱에 주사위 이미지를 추가합니다. 먼저 Dice Roller 앱의 기초를 빌드하는 이전 Codelab을 완료해야 합니다.
TextView
에서 주사위 굴리기 값을 표시하는 대신 앱에서 굴린 면의 숫자에 맞는 주사위 이미지를 표시합니다. 앱에서 훨씬 더 시각적이고 개선된 사용자 환경을 제공할 수 있을 것입니다.
주사위 이미지를 다운로드하는 링크가 제공되고 링크를 앱의 리소스로 추가합니다. 사용할 주사위 이미지의 코드를 작성하려면 Kotlin에서 when
문을 사용합니다.
기본 요건
- 양방향 Dice Roller 앱 만들기 Codelab을 완료함
- 제어 흐름 문(
if / else
,when
문)을 작성할 수 있음 - 사용자 입력에 따라 앱의 UI를 업데이트할 수 있음(
MainActivity.kt
파일 수정) Button.
에 클릭 리스너를 추가할 수 있음- Android 앱에 이미지 리소스를 추가할 수 있음
학습할 내용
- 앱이 실행되는 동안
ImageView
를 업데이트하는 방법 - 다양한 조건에 따라 앱 동작을 맞춤설정하는 방법(
when
문 사용)
빌드할 항목
Button
으로 주사위를 굴리고 화면의 이미지를 업데이트하는 Dice Roller Android 앱
필요한 항목
- Android 스튜디오가 설치된 컴퓨터
- 주사위 이미지를 다운로드하기 위한 인터넷 연결
2. 앱 레이아웃 업데이트
이 작업에서는 레이아웃의 TextView
를 주사위 굴리기 결과의 이미지를 표시하는 ImageView
로 바꿉니다.
Dice Roller 앱 열기
- Android 스튜디오의 이전 Codelab에서 Dice Roller 앱을 열고 실행합니다. 솔루션 코드나 직접 만든 코드를 사용할 수 있습니다.
앱은 다음과 같이 표시됩니다.
activity_main.xml
(app > res > layout > activity_main.xml)을 엽니다. 그러면 Layout Editor가 열립니다.
TextView 삭제
- Layout Editor의 Component Tree에서
TextView
를 선택합니다.
- 마우스 오른쪽 버튼을 클릭하고 Delete를 선택하거나
Delete
키를 누릅니다. Button
에 관한 경고는 현재로서는 무시합니다. 다음 단계에서 이 문제를 해결합니다.
레이아웃에 ImageView 추가
- Palette에서 Design 뷰로
ImageView
를 드래그하여Button
위에 배치합니다.
- Pick a Resource 대화상자의 Sample data 아래에서 avatars를 선택합니다. 다음 작업에서 주사위 이미지를 추가할 때까지 사용할 임시 이미지입니다.
- OK를 누릅니다. 앱의 Design 뷰가 다음과 같이 표시됩니다.
- Component Tree에 오류 두 개가 표시됩니다.
Button
은 세로로 제한되지 않고ImageView
는 세로로도 가로로도 제한되지 않습니다.
Button
이 세로로 제한되지 않는 이유는 원래 그 위에 배치되었던 TextView
를 삭제했기 때문입니다. 이제 ImageView
와 그 아래에 Button
을 배치해야 합니다.
ImageView와 Button 배치
Button
의 위치와 상관없이 ImageView
를 화면에서 세로로 가운데에 배치해야 합니다.
ImageView
에 가로 제약 조건을 추가합니다.ImageView
의 왼쪽을 상위ConstraintLayout
의 왼쪽 가장자리에 연결합니다.ImageView
의 오른쪽을 상위 요소의 오른쪽 가장자리에 연결합니다. 이렇게 하면 상위 요소 내에ImageView
가 가로로 가운데에 배치됩니다.
ImageView
에 세로 제약 조건을 추가하여ImageView
의 상단을 상위 요소의 상단에 연결합니다.ImageView
가ConstraintLayout
의 상단으로 위로 슬라이드됩니다.
Button
에 세로 제약 조건을 추가하여Button
의 상단을ImageView
의 하단에 연결합니다.Button
이ImageView
아래까지 위로 슬라이드됩니다.
- 이제
ImageView
를 다시 선택하고ImageView
의 하단을 상위 요소의 하단에 연결하는 세로 제약 조건을 추가합니다. 이렇게 하면ImageView
가ConstraintLayout
에서 세로로 가운데에 배치됩니다.
제약 조건에 관한 모든 경고가 이제 사라집니다.
이제 Design 뷰는 다음과 같이 표시되며 ImageView
가 가운데에 있고 그 바로 아래에 Button
이 있습니다.
Component Tree에서 ImageView
에 콘텐츠 설명을 추가하라는 ImageView
에 관한 경고가 표시될 수 있습니다. 지금은 이 경고를 걱정하지 않아도 됩니다. 이 Codelab의 후반부에서 표시할 주사위 이미지에 따라 ImageView
의 콘텐츠 설명을 설정하기 때문입니다. 이 변경은 Kotlin 코드에서 실행됩니다.
3. 주사위 이미지 추가
이 작업에서는 주사위 이미지를 다운로드하여 앱에 추가합니다.
주사위 이미지 다운로드
- 이 URL을 열어 주사위 이미지 ZIP 파일을 컴퓨터에 다운로드합니다. 다운로드가 완료될 때까지 기다립니다.
- 컴퓨터에서 파일을 찾습니다(예: Downloads 폴더).
- ZIP 파일을 더블클릭하여 압축을 해제합니다. 그러면 주사위 이미지 파일 6개가 포함된 새
dice_images
폴더가 만들어져 1에서 6까지의 주사위 값을 표시합니다.
앱에 주사위 이미지 추가
- Android 스튜디오의 메뉴에서 View > Tool Windows > Resource Manager를 클릭하거나 Project 창 왼쪽에 있는 Resource Manager 탭을 클릭합니다.
- Resource Manager 아래의 +를 클릭하고 Import Drawables를 선택합니다. 파일 브라우저가 열립니다.
- 주사위 이미지 파일 6개를 찾아 선택합니다. 첫 번째 파일을 선택한 후
Shift
키를 누른 상태에서 나머지 파일을 선택할 수 있습니다. - Open을 클릭합니다.
- Next를 클릭하고 Import를 클릭하여 이러한 6개의 리소스 가져오기를 확인합니다.
- 파일 가져오기가 완료되면 이미지 6개가 앱의 Resource Manager(app>res>drawable)에 표시됩니다.
훌륭합니다. 다음 작업에서는 앱에서 이러한 이미지를 사용합니다.
중요! - 이러한 이미지는 리소스 ID로 Kotlin 코드에서 참조할 수 있습니다.
R.drawable.dice_1
R.drawable.dice_2
R.drawable.dice_3
R.drawable.dice_4
R.drawable.dice_5
R.drawable.dice_6
4. 주사위 이미지 사용
샘플 아바타 이미지 바꾸기
- Design Editor에서
ImageView
를 선택합니다. - Declared Attributes 섹션의 Attributes에서 아바타 이미지로 설정된 도구 srcCompat 속성을 찾습니다.
도구 srcCompat 속성은 Android 스튜디오의 Design 뷰 내에서만 제공된 이미지를 사용합니다. 이미지는 앱을 빌드할 때만 개발자에게 표시되고 실제로 에뮬레이터나 기기에서 앱을 실행할 때는 표시되지 않습니다.
- 아바타의 작은 미리보기를 클릭합니다. 그러면
ImageView
에 사용할 새 리소스를 선택하는 대화상자가 열립니다.
dice_1
드로어블을 선택하고 OK를 클릭합니다.
잘했습니다. ImageView
가 전체 화면을 차지합니다.
다음으로 ImageView
의 너비와 높이를 조정하여 Button
이 숨겨지지 않도록 합니다.
- Constraints Widget의 Attributes 창에서 layout_width 및 layout_height 속성을 찾습니다. 이 두 속성은 현재 wrap_content로 설정되어 있습니다. 즉,
ImageView
의 높이와 너비가 그 안에 있는 콘텐츠(소스 이미지)의 높이 및 너비와 같습니다. - 대신
ImageView
에서 고정 너비 160dp와 고정 높이 200dp로 설정합니다. Enter를 누릅니다.
ImageView
가 이제 훨씬 작습니다.
Button
이 이미지에 너무 가까울 수 있습니다.
- Constraint Widget에서 설정하여 버튼에 상단 여백 16dp를 추가합니다.
Design 뷰가 업데이트되면 앱이 훨씬 더 보기 좋아집니다.
버튼을 클릭할 때 주사위 이미지 변경
레이아웃이 수정되었지만 MainActivity
클래스는 주사위 이미지를 사용하도록 업데이트해야 합니다.
현재 앱의 MainActivity.kt
파일에 오류가 있습니다. 앱을 실행하려고 하면 다음과 같은 빌드 오류가 표시됩니다.
이는 레이아웃에서 삭제한 TextView
를 여전히 코드에서 참조하기 때문입니다.
MainActivity.kt
(app > java > com.example.diceroller > MainActivity.kt)를 엽니다.
코드에서 R.id.textView
를 참조하지만 Android 스튜디오에서는 인식하지 못합니다.
rollDice()
메서드 내에서TextView
를 참조하는 코드를 선택하고 삭제합니다.
// Update the TextView with the dice roll
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = dice.roll().toString()
rollDice()
내에서ImageView
유형의diceImage
라는 새 변수를 만듭니다. 레이아웃의ImageView
와 동일하게 설정합니다.findViewById()
메서드를 사용하고ImageView
의 리소스 IDR.id.imageView
를 입력 인수로 전달합니다.
val diceImage: ImageView = findViewById(R.id.imageView)
ImageView
의 정확한 리소스 ID를 파악하는 방법이 궁금하다면 Attributes 창 상단의 id를 확인하세요.
Kotlin 코드에서 이 리소스 ID를 참조할 때는 정확히 동일하게 입력해야 합니다(소문자 i, 대문자 V, 공백 없음). 정확히 입력하지 않으면 Android 스튜디오에 오류가 표시됩니다.
- 이 코드 줄을 추가하여 버튼을 클릭할 때
ImageView
를 올바르게 업데이트할 수 있는지 테스트합니다. 주사위 굴리기 값이 항상 '2'가 되지는 않지만 테스트 목적으로dice_2
이미지를 사용합니다.
diceImage.setImageResource(R.drawable.dice_2)
이 코드는 ImageView
에서 setImageResource()
메서드를 호출하여 dice_2
이미지의 리소스 ID를 전달합니다. 이렇게 하면 화면의 ImageView
가 업데이트되어 dice_2
이미지가 표시됩니다.
rollDice() 메서드는 이제 다음과 같이 표시됩니다.
private fun rollDice() {
val dice = Dice(6)
val diceRoll = dice.roll()
val diceImage: ImageView = findViewById(R.id.imageView)
diceImage.setImageResource(R.drawable.dice_2)
}
- 앱을 실행하여 오류 없이 실행되는지 확인합니다. 앱은 Roll 버튼을 제외하고 빈 화면으로 시작됩니다.
버튼을 탭하면 값 2를 표시하는 주사위 이미지가 나타납니다. 좋습니다.
버튼 탭에 따라 이미지를 변경할 수 있었습니다. 목표에 가까워지고 있습니다.
5. 주사위 굴리기에 따라 올바른 주사위 이미지 표시
주사위 굴리기 결과가 항상 2가 되지 않는 것은 분명합니다. 다양한 주사위 굴리기의 조건부 동작 추가 Codelab에서 배운 제어 흐름 로직을 사용하여 랜덤 주사위 굴리기에 따라 적절한 주사위 이미지가 화면에 표시되도록 합니다.
코드를 입력하기 전에 발생해야 하는 작업을 설명하는 의사코드를 작성하여 앱의 동작 방식에 관해 개념적으로 생각해 보세요. 예:
사용자가 1을 굴리면 dice_1
이미지를 표시합니다.
사용자가 2를 굴리면 dice_2
이미지를 표시합니다.
등...
위의 의사코드는 주사위 굴리기 값에 따라 Kotlin에서 if / else
문으로 작성할 수 있습니다.
if (diceRoll == 1) {
diceImage.setImageResource(R.drawable.dice_1)
} else if (diceRoll == 2) {
diceImage.setImageResource(R.drawable.dice_2)
}
...
그러나 각 사례에 if / else
를 작성하면 반복이 상당히 많아집니다. 동일한 로직을 when
문을 사용하여 더 간단하게 표현할 수 있습니다. 더 간결합니다(코드가 적음). 앱에서 이 접근 방식을 사용하세요.
when (diceRoll) {
1 -> diceImage.setImageResource(R.drawable.dice_1)
2 -> diceImage.setImageResource(R.drawable.dice_2)
...
rollDice() 메서드 업데이트
rollDice()
메서드에서 이미지 리소스 ID를 매번dice_2
이미지로 설정하는 코드 줄을 삭제합니다.
diceImage.setImageResource(R.drawable.dice_2)
diceRoll
값에 따라ImageView
를 업데이트하는when
문으로 바꿉니다.
when (diceRoll) {
1 -> diceImage.setImageResource(R.drawable.dice_1)
2 -> diceImage.setImageResource(R.drawable.dice_2)
3 -> diceImage.setImageResource(R.drawable.dice_3)
4 -> diceImage.setImageResource(R.drawable.dice_4)
5 -> diceImage.setImageResource(R.drawable.dice_5)
6 -> diceImage.setImageResource(R.drawable.dice_6)
}
변경을 완료하면 rollDice()
메서드가 다음과 같이 표시됩니다.
private fun rollDice() {
val dice = Dice(6)
val diceRoll = dice.roll()
val diceImage: ImageView = findViewById(R.id.imageView)
when (diceRoll) {
1 -> diceImage.setImageResource(R.drawable.dice_1)
2 -> diceImage.setImageResource(R.drawable.dice_2)
3 -> diceImage.setImageResource(R.drawable.dice_3)
4 -> diceImage.setImageResource(R.drawable.dice_4)
5 -> diceImage.setImageResource(R.drawable.dice_5)
6 -> diceImage.setImageResource(R.drawable.dice_6)
}
}
- 앱을 실행합니다. Roll 버튼을 클릭하면 주사위 이미지가 2를 제외한 다른 값으로 변경됩니다. 잘했습니다.
코드 최적화
훨씬 더 간결한 코드를 작성하려면 다음과 같이 코드를 변경하면 됩니다. 앱의 사용자에게 눈에 띄는 영향을 미치지는 않지만 코드가 더 짧아지고 반복성도 줄어듭니다.
when 문에 diceImage.setImageResource()
호출이 6번 표시됩니다.
when (diceRoll) {
1 -> diceImage.setImageResource(R.drawable.dice_1)
2 -> diceImage.setImageResource(R.drawable.dice_2)
3 -> diceImage.setImageResource(R.drawable.dice_3)
4 -> diceImage.setImageResource(R.drawable.dice_4)
5 -> diceImage.setImageResource(R.drawable.dice_5)
6 -> diceImage.setImageResource(R.drawable.dice_6)
}
각 사례 간에 변경되는 유일한 내용은 사용되는 리소스 ID입니다. 즉, 사용할 리소스 ID를 저장할 변수를 만들 수 있습니다. 그리고 코드에서 diceImage.setImageResource()
를 한 번만 호출하여 올바른 리소스 ID를 전달할 수 있습니다.
- 위의 코드를 다음으로 바꿉니다.
val drawableResource = when (diceRoll) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
6 -> R.drawable.dice_6
}
diceImage.setImageResource(drawableResource)
여기서 새로운 개념은 when
표현식이 실제로 값을 반환할 수 있다는 점입니다. 이 새로운 코드 스니펫으로 when
표현식은 올바른 리소스 ID를 반환하고 이 ID는 drawableResource
변수에 저장됩니다. 그러면 이 변수를 사용하여 표시된 이미지 리소스를 업데이트할 수 있습니다.
when
은 이제 빨간색 밑줄이 그어집니다. 포인터를 그 위로 가져가면 다음과 같은 오류 메시지가 표시됩니다. 'when' 표현식은 완전해야 합니다. 필요한 'else' 분기를 추가하세요.
오류가 발생한 이유는 when
표현식의 값이 drawableResource
에 할당되기 때문입니다. 따라서 when
은 완전해야 합니다. 가능한 모든 사례를 처리하여 12면 주사위로 변경하더라도 항상 값이 반환되도록 해야 합니다. Android 스튜디오에서는 else
분기 추가를 제안합니다. 6
의 사례를 else
로 변경하여 이 문제를 해결할 수 있습니다. 1
부터 5
까지의 사례는 동일하지만 6
을 포함한 다른 모든 사례는 else
로 처리합니다.
val drawableResource = when (diceRoll) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
diceImage.setImageResource(drawableResource)
- 앱을 실행하여 여전히 올바르게 작동하는지 확인합니다. 충분히 테스트하여 1에서 6까지 주사위 이미지로 모든 숫자가 표시되는지 확인해야 합니다.
ImageView에서 적절한 콘텐츠 설명 설정
이제 굴려진 숫자를 이미지로 바꿨으므로 스크린 리더가 굴려진 숫자가 무엇인지 더 이상 알 수 없습니다. 이 문제를 해결하려면 이미지 리소스를 업데이트한 후 ImageView
의 콘텐츠 설명을 업데이트합니다. 콘텐츠 설명은 스크린 리더가 설명할 수 있도록 ImageView
에 표시되는 내용의 텍스트 설명이어야 합니다.
diceImage.contentDescription = diceRoll.toString()
스크린 리더는 콘텐츠 설명을 큰 소리로 읽을 수 있으므로 '6' 이미지의 주사위 굴리기가 화면에 표시되면 콘텐츠 설명은 '6'으로 크게 읽힙니다.
6. 적절한 코딩 사례 채택
더 유용한 시작 환경 만들기
사용자가 처음 앱을 열면 앱이 비어 있어(Roll 버튼 제외) 이상하게 보입니다. 사용자가 무엇을 할지 모를 수 있으므로 앱을 처음 시작하여 Activity
를 만들 때 랜덤 주사위 굴리기를 표시하도록 UI를 변경합니다. 그러면 사용자는 Roll 버튼을 탭하면 주사위 굴리기가 생성될 거라고 더 쉽게 이해할 수 있습니다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener { rollDice() }
// Do a dice roll when the app starts
rollDice()
}
코드에 주석 달기
코드에 주석을 추가하여 작성한 코드에서 발생하는 작업을 설명합니다.
이러한 변경을 모두 완료하면 rollDice()
메서드가 다음과 같이 표시될 수 있습니다.
/**
* Roll the dice and update the screen with the result.
*/
private fun rollDice() {
// Create new Dice object with 6 sides and roll the dice
val dice = Dice(6)
val diceRoll = dice.roll()
// Find the ImageView in the layout
val diceImage: ImageView = findViewById(R.id.imageView)
// Determine which drawable resource ID to use based on the dice roll
val drawableResource = when (diceRoll) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
// Update the ImageView with the correct drawable resource ID
diceImage.setImageResource(drawableResource)
// Update the content description
diceImage.contentDescription = diceRoll.toString()
}
전체 MainActivity.kt
파일을 확인하려면 다음 단계에 연결된 GitHub의 솔루션 코드를 참고하세요.
Dice Roller 앱을 완성했습니다! 이제 이 앱을 친구들과 함께 즐길 수 있습니다.
7. 솔루션 코드
이 Codelab의 솔루션 코드는 아래에 나온 프로젝트와 모듈에 있습니다.
이 Codelab의 코드를 가져와서 Android 스튜디오에서 열려면 다음을 실행합니다.
코드 가져오기
- 제공된 URL을 클릭합니다. 브라우저에서 프로젝트의 GitHub 페이지가 열립니다.
- 프로젝트의 GitHub 페이지에서 Code 버튼을 클릭하여 대화상자를 엽니다.
- 대화상자에서 Download ZIP 버튼을 클릭하여 컴퓨터에 프로젝트를 저장합니다. 다운로드가 완료될 때까지 기다립니다.
- 컴퓨터에서 파일을 찾습니다(예: Downloads 폴더).
- ZIP 파일을 더블클릭하여 압축을 해제합니다. 프로젝트 파일이 포함된 새 폴더가 만들어집니다.
Android 스튜디오에서 프로젝트 열기
- Android 스튜디오를 시작합니다.
- Welcome to Android Studio 창에서 Open an existing Android Studio project를 클릭합니다.
참고: Android 스튜디오가 이미 열려 있는 경우 File > New > Import Project 메뉴 옵션을 대신 선택합니다.
- Import Project 대화상자에서 압축 해제된 프로젝트 폴더가 있는 위치로 이동합니다(예: Downloads 폴더).
- 프로젝트 폴더를 더블클릭합니다.
- Android 스튜디오가 프로젝트를 열 때까지 기다립니다.
- Run 버튼 을 클릭하여 앱을 빌드하고 실행합니다. 예상대로 작동하는지 확인합니다.
- Project 도구 창에서 프로젝트 파일을 살펴보고 앱이 설정된 방식을 확인합니다.
8. 요약
setImageResource()
를 사용하여ImageView
에 표시되는 이미지를 변경합니다.if / else
표현식이나when
표현식과 같은 제어 흐름 문을 사용하여 앱에서 다양한 사례를 처리합니다(예: 여러 상황에서 다양한 이미지 표시).
9. 자세히 알아보기
10. 연습
다음을 따르세요.
- 앱에 다른 주사위를 추가하여 Roll 버튼 하나로 주사위 두 개의 결과를 제공하도록 합니다. 레이아웃에
ImageViews
가 몇 개 필요한가요?MainActivity.kt
코드에 어떤 영향을 미치나요?
학습 내용 확인:
완성된 앱은 오류 없이 실행되고 주사위 2개가 표시되어야 합니다.