냐냐한 Dev Study/Android

[학습] 주사위 굴리기 Android 앱 만들기 (Kotlin)

소소하냐 2023. 1. 9. 18:03
안드로이드 공식 문서, Android Basics in Kotlin 과정을 학습하며 기록하고 및 요약하기 위한 글입니다. 

 

"[학습] Android Basics in Kotlin 과정 소개" > Unit 1: Kotlin 기초 > "App에 버튼 생성"  중 다음 내용을 요약했습니다. 

 

- Kotlin의 Classe, object instance (원문, 원문 최종 업데이트일: 2021.07.10)
- 대화형 Dice Roller App 만들기 (원문, 원문 최종 업데이트일: 2021.03.30)
- Kotlin에 조건부 동작 추가 (원문, 원문 최종 업데이트일: 2022.04.09)
- Dice Roller App에 이미지 추가 (원문, 원문 최종 업데이트일: 2021.11.24)


학습일: 2023.01.06 ~ 01.09  

 

- 앱 소개: 주사위를 굴려 랜덤한 결과를 얻는 앱 

- 사용 요소: ConstraintLayout, Button, ImageView

- 주요 학습 내용

  • Class를 정의하고, 해당 Class의 객체를 생성하고 사용 
  • 조건문(when) 사용
  • IntRange 데이터 타입과, random() 함수 사용
  • 이미지 리소스 추가 및 이미지 업데이트
  • 버튼에 클릭 이벤트 추가 

 

- 앱 화면

완성된 앱 화면

 

새 프로젝트 만들기 

----------------------------------------------------------------------------------------------
- Empty Activity 템플릿 선택 
- Name : Dice Roller
- Language: Kotlin
- Minumum SDK : API 19: Android 4.4 (KitKat)
----------------------------------------------------------------------------------------------

 

앱 레이아웃 만들기 

필요한 View 요소는 아래와 같습니다. 

  • 주사위 모양 ImageView
  • [굴리기] Button

 

문자열 리소스 추가 및 변경

app/res/values/strings.xml 

<resources>
    <string name="app_name">주사위 굴리기</string>
    <string name="roll">굴리기</string>
</resources>

 

이미지(drawable) 리소스 추가

1. 이미지 다운로드 URL을 열어 6개의 주사위 이미지가 있는 ZIP 파일을 다운로드 후 압축을 해제합니다. 

 

2. View > Tool Windows > Resource Manager (또는 안드로이드 스튜디오 왼쪽 Resource Manager 탭 클릭) 

 

3. Resource Manager 창에서 + 를 클릭하고, Import Drawables을 선택합니다. 

 

Resource Manager 창에서 + 를 클릭

 

4. Import할 이미지(위에서 다운로드 받은 이미지 6개)를 선택하여 Import 합니다. 

 

5. Resource Manager(app>res>drawable)에 6개의 이미지가 표시됩니다. 

 

Resource Manager에 추가된 이미지

 

위 이미지들은 Kotlin 코드에서 Resource ID로 참조 가능합니다.

(R.drawable.dice_1 ~ R.drawable.dice_6)

 

최종 레이아웃 코드 

Design 탭에서 레이아웃을 배치하는 방법은 원문을 통해 확인하실 수 있습니다.

저는 해당 속성들의 최종 Code를 공유하는 것으로 대신하겠습니다. 

 

app > res> layout > activity_main.xml 파일 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!-- [주사위 이미지] ImageView 속성 설정 -->
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="160dp"
        android:layout_height="200dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@drawable/dice_1" />

    <!-- [굴리기] Button 속성 설정  -->
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@string/roll"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

tools:srcCompat 속성에 대한 참고 사항

- ImageView의 경우 Design > Attributes에는 두 개의 srcCompat 속성이 있습니다. 그중 하나에는 도구 아이콘이 있습니다. 

- 도구 아이콘이 있는 srcCompat 속성Design 탭에서만 미리보기(위치 배치하는) 용도로 표시되는 것이며, 실제로 에뮬레이터나 기기에서 앱을 실행할 때는 표시되지 않습니다.

  (도구 아이콘이 없는 srcCompat 속성의 경우 미리 보기는 물론 앱 실행시에도 표시됩니다.)

- 도구 아이콘이 없는 srcCompat 설정은 Code 상에서는 app:srcCompat="@drawable/dice_1" 로 설정됩니다.

- 도구 아이콘이 있는 srcCompat 설정은 Code 상에서는 tools:srcCompat="@drawable/dice_1"로 설정됩니다. 

두 개의 srcCompat 속성, 아래 srcCompat에는 도구 모양 표시

- 추가로 TextView의 text의 경우도 실제 표시되는 text와 미리보기 용도의 도구 아이콘이 있는 text가 있습니다.

- Code 상에서도 android:text, tools:text 로 각각 설정됩니다. 

- 도구 아이콘이 있는 text의 경우 앱 개발자에게만 보이는 화면으로 문자열 리소스로 추출할 필요가 없습니다. 

두 개의 text 속성, 아래 text에는 도구 모양 표시

 

 

앱 기능 구현 

MainActivity.kt 파일을 엽니다. (app > java > com.example.diceroller > MainActivity.kt)

 

최종 코드 및 주석 

package com.example.diceroller

import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity

/**
 * 이 Activity는 사용자가 주사위를 굴리고 결과를 화면에 표시합니다.
 */
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // [굴리기] 버튼에 클릭 이벤트 설정
        // 1. 버튼 참조 가져오기
        var rollButton: Button = findViewById(R.id.button)
        // 2. 버튼에 클릭 이벤트 리스너 설정
        rollButton.setOnClickListener { rollDice() }
        
        // 앱이 시작될 때 주사위 굴리기
        // 이 부분은 없어도 되지만, 첫 실행 화면에 어떤 앱인지 보여주기 위해서 최소 실행시도 주사위 굴리기를 실행하도록 함
        rollDice()
    }

    /**
     * 주사위를 굴리고 결과를 화면에 업데이트합니다.
     */
    private fun rollDice() {
        // 6면인 주사위(Dice) 객체를 생성하고 굴립니다.
        val dice = Dice(6) // 1. 주사위 객체 생성
        val diceRoll = dice.roll()  // 2. 주사위 굴리기

        // --------------------------------------------------

        // 주사위를 굴린 결과를 화면에 업데이트합니다.
        // 1. ImageView 참조 가져오기
        val diceImage: ImageView = findViewById(R.id.imageView)

        // 2. 주사위 굴린 결과에 따른 리소스 설정
        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
        }

        // 3. ImageView 이미지 업데이트
        diceImage.setImageResource(drawableResource)
        // 4. 스크린 리더가 읽을 수 있더록 이미지에 대한 설명을 추가
        diceImage.contentDescription = diceRoll.toString()
    }
}

/**
 * 고정된 면을 갖는 주사위(Dice) Class
 */
class Dice(private val numSides: Int) {
    /**
     * 주사위를 굴리고 랜덤 결과를 반환
     */
    fun roll(): Int {
        return (1..numSides).random()
    }
}

 

코드 간략 설명 

MainActivity에는 main() 함수가 없습니다.

앞서 모든 Kotlin 프로그램에는 main() 함수가 있어야 한다고 했습니다. Android 앱은 다르게 작동합니다.

main() 함수를 호출하는 대신 Android 시스템은 앱이 처음 열릴 때 MainActivityonCreate() 메서드를 호출합니다. 

 

[굴리기] 버튼에 클릭 이벤트 설정

// 1. 버튼 참조 가져오기

: findViewById(R.id.버튼ID)

// 2. 버튼에 클릭 이벤트 리스너 설정

: 버튼변수.setOnClickListener { 클릭시 작업할 내용 }

 

참고. setContentView 호출 이후에 findViewById를 사용해야 합니다. 그렇지 않은 경우 NullPointerException이 발생합니다.

 

고정된 면을 갖는 주사위(Dice) Class 정의

- class 클래스명(인수) { }: Class 정의 

   class 키워드를 사용, 클래스명은 대문자 카멜 표기법(예:Dice, Car, CustomerRecord)

   예: class Dice { }, class Dice(private val numSides: Int) { }

 

- (시작수..끝수): IntRange 데이터 타입으로, 시작수 부터 끝수까지 정수의 범위를 나타냅니다. 

   예: val range = 1..6 

 

- (시작수..끝수).random(): Kotlin에 내장된 random() 함수를 사용하여, 주어진 범위의 랜덤 숫자를 생성하고 반환합니다. 

   예: (100..200).random(), (1..numSides).random()

 

ImageView 이미지 업데이트 

// 1. ImageView 참조 가져오기

: findViewById(R.id.ImageView의ID)

// 3. ImageView 이미지 업데이트

: 이미지참조변수.setImageResource(리소스) 

// 4. 스크린 리더가 읽을 수 있더록 이미지에 대한 설명을 추가

: 이미지참조변수.contentDescription = "이미지에 대한 설명"

 

조건문: if, else if, else 사용 예 

val num = 4
if (num > 4) {
    println("The variable is greater than 4")
} else if (num == 4) {
    println("The variable is equal to 4")
} else {
    println("The variable is less than 4")
}

 

조건문: when 사용 예 

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
        }

 

참고: 변수에 할당할 경우, else-> 식이 추가되어야 조건에 맞지 않는 경우까지 완전하게 할당할 수 있습니다. 

 

추가 설명 

Activity 

- 앱이 UI를 그리는 창을 제공합니다. 

- 일반적으로 Activity는 실행되는 앱의 전체 화면을 차지합니다. 

- 모든 앱에는 하나 이상의 Activity가 있습니다. 

- 앱은 기본적으로 MainActivity에서 시작됩니다. (시작 Activity 설정은 변경 가능합니다.) 

- 이번 실습인 "주사위 굴리기 앱"에서는 주사위를 굴리는 Button과 결과를 표시하는 Activity가 하나 있습니다. 

 

 

 

끝까지 읽어 주셔서 감사합니다. ^^