
레이아웃

실행 영상
NumberPicker 의 값을 로또 추첨에 사용되는 범위(1~45)로 지정하기
뽑힌 번호 숫자에 맞춰 실제 로또 공이랑 같은 색의 배경이 나오도록 지정하기
(1-10 : 노랑 11-20 파랑 21-30 빨강 31-40 회색 41-45 녹색)
번호 추가하기 버튼눌렀을때
자동 생성하기 버튼 눌렀을 때 사용자가 선택한 숫자를 제외한 숫자 들 중 랜덤한 숫자를 골라주기
초기화 버튼 눌렀을 때 초기화 되게 만들기
뷰바인딩 형식으로 레이아웃과 코드를 연결해준다.
build.gradle.kts(Module :app) 에서 buildFeatures **{** viewBinding = true 추가
MainActivity.kt 에
binding = ActivityMainBinding.inflate(*layoutInflater*)
setContentView(binding.*root*)
레이아웃에서 사용할 Button , NumberPicker 등을 가져온다.
private val clearButton by lazy {binding.btnClear}
private val addButton by lazy {binding.btnAdd}
private val runButton by lazy {binding.btnRun}
private val numPick by lazy {binding.npNum}
private val numTextViewList : List<TextView> by lazy {
listOf<TextView>(
binding.tvNum1,
binding.tvNum2,
binding.tvNum3,
binding.tvNum4,
binding.tvNum5,
binding.tvNum6
)
프로그램이 실행 중인지 확인할 didRun 과 로또 번호가 몇 개 선택 됐는지 확인할 pickNumberSet 변수를 만든다
private var didRun = false
private val pickNumberSet = hashSetOf<Int>()
로또 번호의 범위를 지정한다
numPick.minValue = 1
numPick.maxValue = 45
3개의 버튼들에 대한 함수를 호출해준다.
initAddButton() // 번호 추가하기 버튼
initRunButton() // 자동 생성 시작 버튼
initClearButton() // 초기화 버튼
번호 추가 하기 버튼을 누르면 조건에 따라 토스트 메세지를 띄우거나 번호를 추가하도록 함수를 만들어준다.
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)
}
}
}
}
private fun showToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
초기화 버튼을 누르면 선택된 번호를 삭제하고,처음상태로 돌아가게 만들어준다.
private fun initClearButton() {
clearButton.setOnClickListener {
pickNumberSet.clear()
numTextViewList.forEach { it.isVisible = false }
didRun = false
numPick.value = 1
}
}
자동 생성 하기 버튼을 누르면 번호를 자동으로 생성하도록 만들어준다.
private fun initRunButton() {
runButton.setOnClickListener {
val list = getRandom()
didRun = true
list.forEachIndexed{index, number ->
val textView = numTextViewList[index]
textView.text = number.toString()
textView.isVisible = true
setNumBack(number, textView)
}
}
}
private fun getRandom(): List<Int> {
val numbers = (1 .. 45).filter { it !in pickNumberSet }
return (pickNumberSet + numbers.shuffled().take(6-pickNumberSet.size)).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)
}
xml파일을 직접 만들어서 원하는 도형을 만드는 방법
drawable 폴더에 xml 파일을 새로 만든다.
xml 선언부를 적어준다.
<?xml version="1.0" encoding="utf-8" ?>
xmlns를 통해 기본적인 namespace를 설정하고 rectangle, oval, line ring 중 원하는 모양을 선택한다.
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
속성을 결정해준다
<size
android:width="44dp" // 넓이
android:height="44dp"/> // 높이 <solid
android:color="#1E71FF"/> <stroke
android:color="@color/black" // 테두리 색상
android:width="1dp" // 테두리 선 두께
android:dashGap="5dp" // 점선의 간격
android:dashWidth="1dp" // 점선 길이
/> <corners
android:radius="12dp" //모든 모서리 한번에 적용
android:bottomLeftRadius="15dp"
android:bottomRightRadius="15dp"
android:topLeftRadius="15dp"
android:topRightRadius="15dp" /><gradient
android:type="sweep" // 그라데이션 종류
android:centerX="0.4" // 중심의 x 값
android:centerY="0.6" // 중심의 y 값
android:centerColor="#ffffff" // 가운데 색상
android:startColor="#ff0000" // start로 설정된 부분의 색
android:endColor="#0000ff" // end로 설정된 부분의 색
android:angle="0" //각도
/><padding
android:bottom="10dp"
android:left="10dp"
android:right="10dp"
android:top="10dp" />by lazy
Lazy Initialization (늦은 초기화) 를 하는 방식 중하나
**늦은 초기화** :
컴퓨터 프로그래밍에서 객체 생성, 값 계산 등 비용이 많이 드는 과정을 처음 필요한 시점까지 지연시키는 기법
클래스 안에 '이 변수를 언젠가 쓸거야' 선언만 해놓고, 나중에 실제로 그 값을 사용 하는 시점에 값을 입력하여 사용하는 것
*lateinit* 과 *by lazy* 의 방법이 있다.
hashSetOf
hashSetOf를 알기 위해서는 일단 Collection을 알아야 함
**Collection :**
다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화 된 방법을 제공하는 클래스의 집합
Collection의 Property
﹒size : 컬렉션에 포함된 원소의 개수
Collection의 Method
﹒contains : 해당 요소가 있는지 없는지 체크하는 함수
﹒any : Collection 에 적어도 1개의 요소가 있으면 true를 리턴하는 함수
﹒none : Collection에 1개도 match 되는 요소가 없을때 true를 리턴하는 함수
﹒all : Collection의 모든 요소가 match 될 때 true를 리턴하는 함수
﹒clear : Collection의 모든 기록을 삭제하는 함수
Kotlin은 자체적으로 기본 컬렉션 타입인 List, Set, Map을 제공한다.
이 중 List와 Set은 컬렉션을 상속 받고, Map은 독자적이다.
**Set :**
정의된 순서가 없는 요소 그룹을 나타내는 컬렉션 중복을 허용하지 않는다.
>>중복을 자동으로 제거해줌.
Set의 Method
﹒add : Set에 데이터를 추가하는 함수
﹒add All : Set에 Collection에 포함된 데이터를 추가하는 함수
﹒remove : Set에 데이터를 삭제하는 함수
주로 순서를 유지하고 중복을 허용 해야 할 때에는 List를 사용하고, 요소가 고유하고 요소의 순서가 중요하지 않을 때에는 Set을 사용한다.
hashSetOf 는 새로운 인스턴스를 만드는데 사용되는 함수로
오늘 사용한 코드
private val pickNumberSet = hashSetOf<Int>()
에서는 pickNumberSet 이라는 변수에 Int(정수) 세트를 할당하고, 빈 세트로 시작하겠다는 뜻이다.
forEach
**forEach :**
배열을 순회해서 처리하는데 사용하는 함수.
배열의 각 요소에 대해 주어진 함수를 순서대로 한 번씩 실행함.
forEachIndexed :
forEach와 동일한 기능을 수행하며 value 뿐 아니라 해당 value의 index까지 같이 사용할 수있다.
오늘 사용한 코드에서는
numTextViewList.forEach { it.isVisible = false }
numTextViewList 에 있는 요소들의 isVisible을 하나하나 false로 바꿔주기위해 사용함.
lambda expression (람다식)
**lambda expression :**
method를 하나의 식으로 표현한 것.
익명 함수를 지칭하는 용어
람다 표현식:
1. 람다는 매개변수 화살표(->) 함수몸체로 이용하여 사용 할 수 있다.
2. 함수몸체가 단일 실행문이면 괄호{}를 생략 할 수 있다.
3. 함수몸체가 return문으로만 구성되어 있는 경우 괄호{}를 생략 할 수 없다.
장점 :
불필요한 반복문의 삭제로 복잡한 식을 단순하게 표현 가능하다.
지연연산을 수행함으로써 불필요한 연산을 최소화 할 수 있다.
단점 :
람다식의 호출이 까다롭다.
불필요하게 사용하면 오히려 가독성을 떨어뜨린다.
오늘 사용한 코드에서는 이렇게 사용됐다. (빨간 형광펜 친 부분)
list.forEachIndexed { index, number ->
val textView = numTextViewList[index]
textView.text = number.toString()
textView.isVisible = true
setNumBack(number, textView)
}
private fun getRandom(): List<Int> {
val numbers = (1 .. 45).filter { it !in pickNumberSet }
return (pickNumberSet + numbers.shuffled().take(6-pickNumberSet.size)).sorted()
}
ContextCompat
**ContextCompat :**
여러 버전의 Android 플랫폼에서 일관된 방식으로 작동하도록 도와주는 유틸리티 클래스.
리소스관리, 권한 요청, 레이아웃 인플레이션 등과 같은 작업을 보다 쉽게 수행할 수 있도록 도와주는 여러가지 유틸리티 메서드가 포함되어 있다.
ContextCompat.getDrawable(context, resId) :
메서드는 주어진 컨텍스트와 리소스 ID에 해당하는 Drawable을 반환합니다. 이를 통해 호환성을 유지하면서 리소스에 액세스할 수 있습니다.
오늘 사용한 코드에서는 이렇게 사용했다.
textView.background = ContextCompat.getDrawable(this, background)
주어진 background 리소스ID에 해당하는 Drawable을 가져와서 textView의 배경으로 설정하라는 뜻.