Kotlin 조건부 - 2부

이 Codelab에서는 기존 Dice Roller Android 앱에 주사위 이미지를 추가합니다. 먼저 Dice Roller 앱의 기초를 빌드하는 이전 Codelab을 완료해야 합니다.

TextView에서 주사위 굴리기 값을 표시하는 대신 앱에서 굴린 면의 숫자에 맞는 주사위 이미지를 표시합니다. 앱에서 훨씬 더 시각적이고 개선된 사용자 환경을 제공할 수 있을 것입니다.

주사위 이미지를 다운로드하는 링크가 제공되고 링크를 앱의 리소스로 추가합니다. 사용할 주사위 이미지의 코드를 작성하려면 Kotlin에서 when 문을 사용합니다.

기본 요건

  • 버튼으로 Dice Roller Android 앱 만들기 Codelab을 완료함
  • 제어 흐름 문(if / else, when 문)을 작성할 수 있음
  • 사용자 입력에 따라 앱의 UI를 업데이트할 수 있음(MainActivity.kt 파일 수정)
  • Button.에 클릭 리스너를 추가할 수 있음
  • Android 앱에 이미지 리소스를 추가할 수 있음

학습할 내용

  • 앱이 실행되는 동안 ImageView를 업데이트하는 방법
  • 다양한 조건에 따라 앱 동작을 맞춤설정하는 방법(when 문 사용)

빌드할 항목

  • Button으로 주사위를 굴리고 화면의 이미지를 업데이트하는 Dice Roller Android 앱

필요한 항목

  • Android 스튜디오가 설치된 컴퓨터
  • 주사위 이미지를 다운로드하기 위한 인터넷 연결

이 작업에서는 레이아웃의 TextView를 주사위 굴리기 결과의 이미지를 표시하는 ImageView로 바꿉니다.

Dice Roller 앱 열기

  1. Android 스튜디오에서 버튼으로 Dice Roller Android 앱 만들기에서 Dice Roller 앱을 열고 실행합니다.
    앱은 다음과 같이 표시됩니다.

  1. activity_main.xml(app > res > layout > activity_main.xml)을 엽니다.
    그러면 Layout Editor가 열립니다.

TextView 삭제

  1. Layout EditorComponent Tree에서 TextView를 선택합니다.
  1. 마우스 오른쪽 버튼을 클릭하고 Delete를 선택하거나 Delete 키를 누릅니다.
  2. Button에 관한 경고는 현재로서는 무시합니다. 다음 단계에서 이 문제를 해결합니다.

레이아웃에 ImageView 추가

  1. Palette에서 Design 뷰로 ImageView를 드래그하여 Button 위에 배치합니다.

  1. Pick a Resource 대화상자의 Sample data 아래에서 avatars를 선택합니다. 다음 작업에서 주사위 이미지를 추가할 때까지 사용할 임시 이미지입니다.

  1. OK를 누릅니다. 앱의 Design 뷰가 다음과 같이 표시됩니다.

  1. Component Tree에 오류 두 개가 표시됩니다. Button은 세로로 제한되지 않고 ImageView는 세로로도 가로로도 제한되지 않습니다.

Button이 세로로 제한되지 않는 이유는 원래 그 위에 배치되었던 TextView를 삭제했기 때문입니다. 이제 ImageView와 그 아래에 Button을 배치해야 합니다.

ImageView와 Button 배치

Button의 위치와 상관없이 ImageView를 화면에서 세로로 가운데에 배치해야 합니다.

  1. ImageView에 가로 제약 조건을 추가합니다. ImageView의 왼쪽을 상위 ConstraintLayout의 왼쪽 가장자리에 연결합니다.
  2. ImageView의 오른쪽을 상위 요소의 오른쪽 가장자리에 연결합니다.
    이렇게 하면 상위 요소 내에 ImageView가 가로로 가운데에 배치됩니다.

  1. ImageView에 세로 제약 조건을 추가하여 ImageView의 상단을 상위 요소의 상단에 연결합니다.
    ImageViewConstraintLayout의 상단으로 위로 슬라이드됩니다.
  2. Button에 세로 제약 조건을 추가하여 Button의 상단을 ImageView의 하단에 연결합니다.
    ButtonImageView 아래까지 위로 슬라이드됩니다.
  3. 이제 ImageView를 다시 선택하고 ImageView의 하단을 상위 요소의 하단에 연결하는 세로 제약 조건을 추가합니다.
    이렇게 하면 ImageViewConstraintLayout에서 세로로 가운데에 배치됩니다.

제약 조건에 관한 모든 경고가 이제 사라집니다.

이제 Design 뷰는 다음과 같이 표시되며 ImageView가 가운데에 있고 그 바로 아래에 Button이 있습니다.

Component Tree에서 ImageView에 콘텐츠 설명을 추가하라는 ImageView에 관한 경고가 표시될 수 있습니다. 지금은 이 경고를 걱정하지 않아도 됩니다. 이 Codelab의 후반부에서 표시할 주사위 이미지에 따라 ImageView의 콘텐츠 설명을 설정하기 때문입니다. 이 변경은 Kotlin 코드에서 실행됩니다.

이 작업에서는 주사위 이미지를 다운로드하여 앱에 추가합니다.

주사위 이미지 다운로드

  1. 이 URL을 열어 주사위 이미지 ZIP 파일을 컴퓨터에 다운로드합니다. 다운로드가 완료될 때까지 기다립니다.
  2. 컴퓨터에서 파일을 찾습니다(예: Downloads 폴더).
  3. ZIP 파일을 더블클릭하여 압축을 해제합니다. 그러면 주사위 이미지 파일 6개가 포함된 새 DiceImages 폴더가 만들어져 1에서 6까지의 주사위 값을 표시합니다.

앱에 주사위 이미지 추가

  1. Android 스튜디오의 메뉴에서 View > Tool Windows > Resource Manager를 클릭하거나 Project 창 왼쪽에 있는 Resource Manager 탭을 클릭합니다.
  2. Resource Manager 아래의 +를 클릭하고 Import Drawables를 선택합니다. 파일 브라우저가 열립니다.

  1. 주사위 이미지 파일 6개를 찾아 선택합니다. 첫 번째 파일을 선택한 후 Shift 키를 누른 상태에서 나머지 파일을 선택할 수 있습니다.
  2. Open을 클릭합니다.
  1. Next를 클릭하고 Import를 클릭하여 이러한 6개의 리소스 가져오기를 확인합니다.

  1. 파일 가져오기가 완료되면 앱의 Drawable 목록에 이미지 6개가 표시됩니다.

훌륭합니다. 다음 작업에서는 앱에서 이러한 이미지를 사용합니다.

중요! - 이러한 이미지는 리소스 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

샘플 아바타 이미지 바꾸기

  1. Design Editor에서 ImageView를 선택합니다.
  2. Declared Attributes 섹션의 Attributes에서 아바타 이미지로 설정된 도구 srcCompat 속성을 찾습니다.

도구 srcCompat 속성은 Android 스튜디오의 Design 뷰 내에서만 제공된 이미지를 사용합니다. 이미지는 앱을 빌드할 때만 개발자에게 표시되고 실제로 에뮬레이터나 기기에서 앱을 실행할 때는 표시되지 않습니다.

  1. 아바타의 작은 미리보기를 클릭합니다. 그러면 ImageView에 사용할 새 리소스를 선택하는 대화상자가 열립니다.

  1. dice_1 드로어블을 선택하고 OK를 클릭합니다.

잘했습니다. ImageView가 전체 화면을 차지합니다.

다음으로 ImageView의 너비와 높이를 조정하여 Button이 숨겨지지 않도록 합니다.

  1. Constraints WidgetAttributes 창에서 layout_widthlayout_height 속성을 찾습니다. 이 두 속성은 현재 wrap_content로 설정되어 있습니다. 즉, ImageView의 높이와 너비가 그 안에 있는 콘텐츠(소스 이미지)의 높이 및 너비와 같습니다.
  2. 대신 ImageView에서 고정 너비 160dp와 고정 높이 200dp로 설정합니다. Enter를 누릅니다.

    ImageView가 이제 훨씬 작습니다.


Button이 이미지에 너무 가까울 수 있습니다.

  1. Constraint Widget에서 설정하여 버튼에 상단 여백 16dp를 추가합니다.

Design 뷰가 업데이트되면 앱이 훨씬 더 보기 좋아집니다.

버튼을 클릭할 때 주사위 이미지 변경

레이아웃이 수정되었지만 MainActivity 클래스는 주사위 이미지를 사용하도록 업데이트해야 합니다.

현재 앱의 MainActivity.kt 파일에 오류가 있습니다. 앱을 실행하려고 하면 다음과 같은 빌드 오류가 표시됩니다.

이는 레이아웃에서 삭제한 TextView를 여전히 코드에서 참조하기 때문입니다.

  1. MainActivity.kt(app > java > com.example.diceroller > MainActivity.kt)를 엽니다.

코드에서 R.id.textView를 참조하지만 Android 스튜디오에서는 인식하지 못합니다.

  1. rollDice() 메서드 내에서 TextView를 참조하는 코드를 선택하고 삭제합니다.
// Update the TextView with the dice roll
val resultTextView: TextView = findViewByID(R.id.textView)
resultTextView.text = dice.roll().toString()
  1. rollRice() 내에서 ImageView 유형의 diceImage라는 새 변수를 만듭니다. 레이아웃의 ImageView와 동일하게 설정합니다. findViewById() 메서드를 사용하고 ImageView의 리소스 ID R.id.imageView를 입력 인수로 전달합니다.
val diceImage: ImageView = findViewById(R.id.imageView)

ImageView의 정확한 리소스 ID를 파악하는 방법이 궁금하다면 Attributes 창 상단의 id를 확인하세요.

Kotlin 코드에서 이 리소스 ID를 참조할 때는 정확히 동일하게 입력해야 합니다(소문자 i, 대문자 V, 공백 없음). 정확히 입력하지 않으면 Android 스튜디오에 오류가 표시됩니다.

  1. 이 코드 줄을 추가하여 버튼을 클릭할 때 ImageView를 올바르게 업데이트할 수 있는지 테스트합니다. 주사위 굴리기 값이 항상 '2'가 되지는 않지만 테스트 목적으로 dice_2 이미지를 사용합니다.
diceImage.setImageResource(R.drawable.dice_2)

이 코드는 ImageView에서 setImageResource() 메서드를 호출하여 dice_2 이미지의 리소스 ID를 전달합니다. 이렇게 하면 dice_2 이미지를 표시하도록 화면의 ImageView가 업데이트됩니다.

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)
}
  1. 앱을 실행하여 오류 없이 실행되는지 확인합니다.

앱은 Roll 버튼을 제외하고 빈 화면으로 시작됩니다.

버튼을 탭하면 값 2를 표시하는 주사위 이미지가 나타납니다. 잘했습니다.

버튼 탭에 따라 이미지를 변경할 수 있었습니다. 목표에 가까워지고 있습니다

주사위 굴리기 결과가 항상 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() 메서드 업데이트

  1. rollDice() 메서드에서 이미지 리소스 ID를 매번 dice_2 이미지로 설정하는 코드 줄을 삭제합니다.
diceImage.setImageResource(R.drawable.dice_2)
  1. 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)
   }
}
  1. 앱을 실행합니다. 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를 전달할 수 있습니다.

  1. 위의 코드를 다음으로 바꿉니다.
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 변수에 저장됩니다. 그러면 이 변수를 사용하여 표시된 이미지 리소스를 업데이트할 수 있습니다.

  1. 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. 앱을 실행하여 여전히 올바르게 작동하는지 확인합니다. 충분히 테스트하여 1에서 6까지 주사위 이미지로 모든 숫자가 표시되는지 확인해야 합니다.

ImageView에서 적절한 콘텐츠 설명 설정

이제 굴려진 숫자를 이미지로 바꿨으므로 스크린 리더가 굴려진 숫자가 무엇인지 더 이상 알 수 없습니다. 이 문제를 해결하려면 이미지 리소스를 업데이트한 후 ImageView의 콘텐츠 설명을 업데이트합니다. 콘텐츠 설명은 스크린 리더가 설명할 수 있도록 ImageView에 표시되는 내용의 텍스트 설명이어야 합니다.

diceImage.contentDescription = diceRoll.toString()

스크린 리더는 콘텐츠 설명을 큰 소리로 읽을 수 있으므로 '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 앱을 완성했습니다! 이제 이 앱을 친구들과 함께 즐길 수 있습니다.

이 Codelab의 솔루션 코드는 아래에 나온 프로젝트와 모듈에 있습니다.

GitHub에서 이 Codelab의 코드를 가져와 Android 스튜디오에서 열려면 다음을 실행합니다.

  1. Android 스튜디오를 시작합니다.
  2. Welcome to Android Studio 창에서 Check out project from version control을 클릭합니다.
  3. Git을 선택합니다.

  1. Clone Repository 대화상자에서 제공된 코드 URL을 URL 상자에 붙여넣습니다.
  2. Test 버튼을 클릭하고 기다렸다가 Connection successful이라고 표시되는 녹색 팝업 도움말 풍선이 있는지 확인합니다.
  3. 필요한 경우 Directory를 제안된 기본값과 다른 것으로 변경합니다.

  1. Clone을 클릭합니다. Android 스튜디오에서 코드를 가져오기 시작합니다.
  2. Checkout from Version Control 팝업에서 Yes를 클릭합니다.

  1. Android 스튜디오가 열릴 때까지 기다립니다.
  2. Codelab 시작 코드나 솔루션 코드에 적합한 모듈을 선택합니다.

  1. Run 버튼 을 클릭하여 코드를 빌드하고 실행합니다.
  • setImageResource()를 사용하여 ImageView에 표시되는 이미지를 변경합니다.
  • if / else 표현식이나 when 표현식과 같은 제어 흐름 문을 사용하여 앱에서 다양한 사례를 처리합니다(예: 여러 상황에서 다양한 이미지 표시).

다음을 따르세요.

  1. 앱에 다른 주사위를 추가하여 Roll 버튼 하나로 주사위 두 개의 결과를 제공하도록 합니다. 레이아웃에 ImageViews가 몇 개 필요한가요? MainActivity.kt 코드에 어떤 영향을 미치나요?

학습 내용 확인:

완성된 앱은 오류 없이 실행되고 주사위 2개가 표시되어야 합니다.