ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android 개발 종합반] 2주차 - 로또번호생성기
    안드로이드 2024. 2. 27. 16:46

    ✏️ TIL(Today I Learned)

    app:layout_constraint 관련 속성은 ConstraintLayout에서 뷰를 배치하는 데 사용되는 속성들이다. ConstraintLayout은 다른 뷰들과의 관계에 따라 뷰의 위치와 크기를 지정한다. 이를 위해 app:layout_constraint 속성을 사용하여 뷰 간의 제약 조건을 설정한다. ConstraintLayout은 다양한 디바이스 크기와 화면 방향에 대해 유연하게 대응할 수 있다는 장점이 있다.

    더보기

    app:layout_constraintTop_toTopOf: 뷰의 상단을 다른 뷰의 상단에 맞춘다.
    app:layout_constraintBottom_toBottomOf: 뷰의 하단을 다른 뷰의 하단에 맞춘다.
    app:layout_constraintStart_toStartOf: 뷰의 시작 부분을 다른 뷰의 시작 부분에 맞춘다.
    app:layout_constraintEnd_toEndOf: 뷰의 끝 부분을 다른 뷰의 끝 부분에 맞춘다.


    app:layout_constraintHorizontal_bias: 가로 방향의 정렬을 설정합니다. 0이면 왼쪽 정렬, 1이면 오른쪽 정렬, 0.5이면 가운데 정렬.
    app:layout_constraintVertical_bias: 세로 방향의 정렬을 설정합니다. 0이면 위쪽 정렬, 1이면 아래쪽 정렬, 0.5이면 중앙 정렬.
    app:layout_constraintVertical_weight: 세로 방향의 가중치를 설정.
    app:layout_constraintHorizontal_weight: 가로 방향의 가중치를 설정.
    app:layout_constraintWidth_percent: 부모 뷰의 너비에 대한 비율을 설정.
    app:layout_constraintHeight_percent: 부모 뷰의 높이에 대한 비율을 설정.

    처음 안드로이드에 관해 배울 때 주로 LinearLayout을 많이 사용해서, 주로 버튼 두개를 가로로 배치할 때 주로 아래와 같이 코드를 작성했었다.

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
    
        <Button
            android:id="@+id/btn_run"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="자동 생성" />
    
        <Button
            android:id="@+id/btn_clear"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="초기화" />
    
    </LinearLayout>

     

    오늘의 강의에서는 ConstraintLayout를 사용했는데 버튼을 배치할 때, LinearLayout 다르게 배치하는 방법을 배웠다. 굳이 레이아웃으로 감싸지 않아도 된다는 점이 편리하다.

    <Button
            android:id="@+id/btn_run"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:layout_marginBottom="16dp"
            android:text="자동 생성 시작"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/btn_clear"
            app:layout_constraintStart_toStartOf="parent" />
    
        <Button
            android:id="@+id/btn_clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:layout_marginBottom="16dp"
            android:text="초기화"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/btn_run" />

     

     

    📝 공부한 Kotlin 요약 정리

      • by lazy: Kotlin에서 지연 초기화를 수행하는 데 사용되는 특별한 프로퍼티 위임
        이를 통해 변수를 처음으로 사용할 때까지 초기화를 지연시킬 수 있다. 뷰가 실제로 필요할 때까지 초기화를 수행하지 않아서 자원 소비를 줄이고 성능을 향상시키는 데 유용하다.

      • ContextCompat.getDrawable(): 안드로이드에서 Drawable 리소스를 가져오는 메소드
        다음과 같이 사용하며 context는 현재 컨텍스트를 나타내는 변수이고, R.drawable.my_drawable은 가져오려는 Drawable 리소스의 ID이다.
      • val drawable = ContextCompat.getDrawable(context, R.drawable.my_drawable)
    • when{}: Kotlin의 제어 흐름 구조 중 하나로, Java의 switch 문과 유사한 기능을 제공
      when을 사용하면 다양한 조건을 지정하여 해당하는 블록을 실행할 수 있다. 예를 들어, 정수 값에 따라 다른 동작을 수행하는 코드를 작성할 수 있다. 또한 when은 다양한 타입의 값에 대해 패턴 매칭을 지원하므로, 객체의 타입이나 범위에 따라 다른 동작을 수행할 수도 있다.
      when (조건) {
          값1 -> {
              // 조건이 값1과 일치할 때 실행할 코드
          }
          값2 -> {
              // 조건이 값2와 일치할 때 실행할 코드
          }
          else -> {
              // 어떤 값과도 일치하지 않을 때 실행할 코드
          }
      }

    • forEach{}: Kotlin의 컬렉션에 대한 확장 함수로, 각 요소에 대해 주어진 동작을 수행
      아래 코드의 forEachnumbers 리스트의 각 요소에 대해 주어진 람다 식을 실행해 리스트의 모든 요소가 출력한다.
      val numbers = listOf(1, 2, 3, 4, 5)
      numbers.forEach {
          println(it)
      }
    • forEachIndexed{}: 인덱스와 함께 각 요소에 대해 작업을 수행
      이를 통해 요소의 값 뿐만 아니라 해당 요소의 인덱스에도 접근할 수 있습니다. 아래  코드에서 forEachIndexednumbers 리스트의 각 요소와 해당 요소의 인덱스에 대해 주어진 람다 식을 실행한다.
      val numbers = listOf(1, 2, 3, 4, 5)
      numbers.forEachIndexed { index, value ->
          println("Index: $index, Value: $value")
      }

     

    🔎 전체 코드

    class MainActivity : AppCompatActivity() {
    
        // 각 버튼과 넘버 피커에 대한 참조를 지연 초기화로 선언
        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) }
    
        // 선택된 숫자를 나타내는 텍스트 뷰 리스트
        private val numTextViewList : List<TextView> by lazy {
            listOf<TextView>(
                findViewById(R.id.tv_num1)
                ,findViewById(R.id.tv_num2)
                ,findViewById(R.id.tv_num3)
                ,findViewById(R.id.tv_num4)
                ,findViewById(R.id.tv_num5)
                ,findViewById(R.id.tv_num6))
        }
    
        // 룰렛을 돌린 여부 변수
        private var didRun = false
    
        // 선택된 숫자들의 집합
        private val pickNumSet = hashSetOf<Int>()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            // 넘버 피커의 범위를 설정
            numPick.minValue = 1
            numPick.maxValue = 45
    
            // 각 버튼에 대한 초기화
            initRunButton()
            initAddButton()
            initClearButton()
        }
    
        // 넘버 피커로 번호를 추가하는 버튼 클릭리스너
        private fun initAddButton() {
            addButton.setOnClickListener {
                when {
                    didRun -> showToast("초기화 후에 시도해주세요.")
                    pickNumSet.size >= 6 -> showToast("최대 6개까지 선택할 수 있습니다.")
                    pickNumSet.contains(numPick.value) -> showToast("이미 선택한 숫자입니다.")
                    else -> {
                        // 선택된 숫자를 텍스트 뷰에 표시하고 배경색을 설정
                        val textView = numTextViewList[pickNumSet.size]
                        textView.isVisible = true
                        textView.text = numPick.value.toString()
                        setNumBack(numPick.value, textView)
                        pickNumSet.add(numPick.value)
                    }
                }
            }
        }
    
        // 텍스트 뷰의 배경색을 설정
        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)
        }
    
        // 초기화 버튼 클릭리스너
        private fun initClearButton() {
            clearButton.setOnClickListener {
                // 선택된 숫자들과 텍스트 뷰들을 초기화
                pickNumSet.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 pickNumSet }
            return (pickNumSet + numbers.shuffled().take(6 - pickNumSet.size)).sorted()
        }
    
        // 토스트 메시지
        private fun showToast(message: String) {
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
        }
    }

     

Designed by Tistory.