BMI 계산기에 이어서 이번에는 로또 번호 생성기를 제작해 볼 것이다. 먼저 로또 번호 생성기의 조건을 살펴보자
[ 로또 번호 생성 조건 ]
● 1~45까지 랜덤하게 공이 뽑힐 것
● 랜덤하게 출력되나 뽑힌 공은 다시 나오지 않을 것
● 사용자가 지정해서 뽑은 공의 번호와 개수를 제외하고 랜덤하게 뽑힐 것
[ 로또 번호 생성기 UI - xml ]
(UI 구현 - xml)
1일차에서 사용했던 Common들과 비슷하게 대부분 사용되었지만 새로 사용해 본 것이 두 가지 있다
● LinearLayout : ViewGroup의 하위 클래스로 가로나 세로를 순서대로 배치할 때 사용 (높이, 너비, 방향을 반드시 지정)
● NumberPicker : 숫자 범위를 지정하여 스크롤하며 숫자 하나를 선택할 수 있는 기능
(Activity 구현 - Kotlin)
// 공을 뽑는 기능을 하는 함수
// (예외 조건: 꽉 찬 경우, 공을 5개 넘게 넣는 경우, 이미 선택한 걸 넣는 경우)
private fun initAddButton() {
addButton.setOnClickListener {
when {
didRun -> showToast("초기화 후에 시도해 주세요")
pickNumberSet.size >= 5 -> showToast("숫자는 최대 5개까지 선택할 수 있습니다")
pickNumberSet.contains(numPick.value) -> showToast("이미 선택된 숫자입니다")
else -> { // 예외 조건이 아닌 참인 경우
val textView = numTextViewList[pickNumberSet.size]
textView.isVisible = true
textView.text = numPick.value.toString()
setNumBack(numPick.value, textView)
pickNumberSet.add(numPick.value)
}
}
}
}
ㄴ> 위 코드는 공을 뽑는 기능을 구현하는 함수인 initAddButton이다. 이 기능은 사용자가 지정한 숫자의 공을 뽑는 것이고 예외 조건이 존재한다.
[ 예외 조건 ]
(공이 이미 꽉 찬 경우, 공을 5개 넘게 넣는 경우, 이미 선택한 공을 넣는 경우)
1. 공이 이미 꽉 찬 경우 : didRun은 공이 꽉 찼는지 아닌지를 나타내는 변수이고 기본값은 false이다. 그러나 자동 생성 이후에는 true로 변환되기 때문에 이 변수를 통해서 예외를 체크한다.
2. 공을 5개 넘게 넣는 경우: pickNumberSet.size 를 통해 뽑힌 숫자의 개수를 확인하며 pickNumberSet은 hashSet 타입의 자료구조이다. (hashSet: 중복을 허용하지 않으며 순서가 없는 일정한 자료구조 형태)
3. 이미 선택한 공을 넣는 경우: pickNumberSet.contains(numPick.value) 를 통해 pickNumberSet 안에 numPick.value 와 겹치는 값이 있는지 확인한다.
// 초기화 버튼을 눌렀을 때 공들을 초기화하는 함수
private fun initClearButton() {
clearButton.setOnClickListener {
pickNumberSet.clear()
numTextViewList.forEach{it.isVisible = false}
didRun = false
numPick.value = 1
}
}
ㄴ> 위 코드는 초기화 버튼을 눌렀을 때 pickNumerSet을 clear()하고 forEach() 반복문을 사용하여 공들의 형태를 감춘다. 또한 다른 변수(didRun, numPick.value)들도 초기값으로 변경한다.
// 1부터 45까지 랜덤하게 숫자를 추출
private fun getRandom(): List<Int> {
val numbers = (1..45).filter {it !in pickNumberSet } // filter를 통해 이미 선택된 값을 제외한 1~45
return (pickNumberSet + numbers.shuffled().take(6-pickNumberSet.size)).sorted()
// 사용자가 뽑은 숫자와 전체(6개) 중 사용자가 뽑은 숫자 갯수를 뺀 숫자들을 정렬
}
ㄴ> 위 코드는 1에서 45까지 숫자들을 랜덤하게 추출하나 filter() 함수를 사용하여 이미 선택된 값을 제외하며 반환하는 값은 사용자가 뽑은 숫자 + 전체(6개) - 사용자가 뽑은 숫자와 개수(n개)를 정렬(sorted())하여 반환한다.
// 숫자 범위에 따라 공의 색깔을 지정하는 함수
private fun setNumBack(number: Int, textView: TextView) {
val background = when(number) {
in 1..10 -> R.drawable.circle_yellow
in 11..20 -> R.drawable.circle_blue
in 21..30 -> R.drawable.circle_red
in 31..40 -> R.drawable.circle_gray
else -> R.drawable.circle_green
}
textView.background = ContextCompat.getDrawable(this, background)
}
ㄴ> 위 코드는 앞서 설명한 조건에 맞게 로또 번호의 범위에 따라 공의 색깔을 지정한다.
// Toast 메세지를 띄우는 명령어를 매번 작성하는 번거로움을 없애기 위한 함수
private fun showToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
ㄴ> 위 코드는 로또 생성기와 상관은 없지만 Toast 메세지를 띄우는 명령어를 매번 작성하지 않고 메소드 형식으로 사용할 수 있도록 만든 함수이다.
[ 사용된 Kotlin 문법 간단 정리 (기억할 만한 것 위주) ]
- by lazy 와 lateinit
private val clearButton by lazy { findViewById<Button>(R.id.btn_clear) }
private val addButton by lazy { findViewById<Button>(R.id.btn_add) }
private val runButton by lazy { findViewById<Button>(R.id.btn_run) }
private val numPick by lazy { findViewById<NumberPicker>(R.id.np_num) }
ㄴ> 위 코드를 보면 변수를 선언할 때 by lazy 라는 구문이 붙은 것을 볼 수 있는데 이 구문의 역할은 쉽게 설명하면 늦은 초기화를 위해 사용되는 구문이다. Kotlin은 언어 특성상 null을 허용하지 않기 때문에 초기화가 필수적이다. 이런 초기화를 늦추기 위해 사용하는 것 ( by lazy는 불변 변수인 val 를 사용할 때 쓰고 / lateinit은 가변 변수인 var 를 사용할 때 쓴다 )
'Android' 카테고리의 다른 글
(Android Studio) - 레이아웃 종류와 쓰임새 (0) | 2024.03.19 |
---|---|
(Android Studio) 프로젝트 구조 (1) | 2024.03.18 |
(Android Studio) Activity 와 Fragment 의 차이점 (0) | 2024.02.29 |
(Kotlin) 개발 공부 3일차 - MBTI 테스트 (0) | 2024.02.28 |
(Kotlin) 개발 공부 1일차 - BMI 계산기 (1) | 2024.02.27 |