티스토리 뷰

Android Projects

[난수 생성기] UI & 로직 구현

IT Knowledge Share 2021. 8. 3. 00:26
반응형

1~30 중의 숫자에서 랜덤한 숫자 5개를 뽑는 어플을 제작하는 프로젝트입니다.

 

해당 어플에서 중점적인 부분을 정리하면 다음과 같습니다.

1. UI 부분에서는 텍스트뷰에서 android:visibility 속성을 사용해서 텍스트를 보이고, 감추도록 합니다.

2. 레이아웃은 Constraint Layout을 사용합니다. 레이아웃의 내의 뷰에 제약을 줌으로써 원하는 스타일로 레이아웃을 배치할 수 있습니다.

3. 필요한 변수는 실제 사용 시에 초기화되면서, 값이 할당되도록 lazy init을 사용합니다.

4. 난수를 뽑는 함수는 apply를 함수를 통해서 초기화를 시키는데, 반복문 내에서 이미 뽑은 번호는 그냥 넘어갈 수 있도록 continue로 빠져 나옵니다. 난수를 리턴하기 전까지, 기존에 뽑았던 번호는 제외시킬 수 있도록 기능을 구현합니다.

5. 번호를 직접 추가하는 부분에서 예외 처리를 해줍니다: 최대 선택 가능한 번호, 이미 뽑은 번호, 초기화가 안된 경우!

 

그럼, 실제로 UI와 기능 구현 부분을 확인해 봅시다.

 

먼저, 레이아웃의 일부를 보는데, Constraint Layout를 사용합니다. 레이아웃의 요소에 제약을 줘서 원하는 그림대로 배치시켜 줍니다. 예를 들어, B에 제약을 거는 경우, app:layout_constraintBottom_toTopOf="A" 라면, A 뷰의 위 쪽에 B 뷰의 아랫부분을 비치시킨 다는 의미입니다. 

반응형

난수를 뽑기 위해, NumberPicker라는 자식 뷰를 사용하였으며, 필요한 버튼을 생성해주도록 합니다.

숫자가 표시되는 텍스트뷰에는 android:visibility 속성을 추가하여, 숫자가 사라질 수 있도록 합니다. 테스트 레이아웃에서 숫자가 안보이면 곤란하니깐, tools:visibility="visible" 속성을 추가하여, 테스트 상에서는 보이도록 설정합니다.

<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">

    <NumberPicker
        android:id="@+id/numberPicker"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

        <Button>...</Button>

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:gravity="center"
            android:textColor="@color/black"
            android:textSize="18sp"
            android:visibility="gone"
            tools:text="1"
            tools:visibility="visible" />

          
            ... 이하 생략 ...
</ConstraintLayout>

 

기능 부분을 보면, 우선 어플에는 다음의 4개 버튼을 구현할 것입니다.

initNumberPicker() //1~30까지 숫자 선택하여 뽑는 부분
initRunButton()    //난수 뽑기
initAddButton()    //원하는 숫자 선택하여 추가하기
initClearButton()  //초기화

 

각 기능별로, 메소드를 살펴보도록 합시다. 먼저 숫자를 선택하는 부분은, 간단하게 최소값 및 최대값을 지정해주면 됩니다. 필요한 변수는 onCreate 함수 밖에 전역 변수로 lazy init을 사용하여 미리 선언해두었습니다.

 fun initNumberPicker(){
        numberPicker.minValue = 1
        numberPicker.maxValue = 30
 }

다음은 난수를 뽑는 부분입니다. 난수를 뽑는 함수인 getRandomNumber 메소드를 보면, 리스트 변형이 가능해야 하므로 mutableList를 사용했습니다. 그리고 apply 함수로 초기화를 시키는 과정에서 기존에 뽑았던 번호는 걸러줘야 합니다.

나중에 기존에 뽑았던 번호와 새로 뽑인 번호를 합쳐서 shuffle 함수를 사용한 뒤, 리턴해주면 됩니다.

 

이렇게 뽑인 번호는 list로 들어가는데, forEachIndexed를 사용해서 인덱스와 숫자를 넘겨받도록 합니다. numberTextViewList는 최대 5번째 인덱스까지 번호가 들어가는 초기화 리스트 변수인데, 뽑은 갯수 만큼 리스트로 보여줘야 하므로 [index]로 처리했습니다.

fun initRunButton(){
        runButton.setOnClickListener {
            val list = getRandomNumber()

            ... 생략 ...
            
            list.forEachIndexed { index, number ->
                //뽑은 갯수 만큼 번호 리스트 보여줘야 하니까 [index]로 처리.
                val textView = numberTextViewList[index]

             ... 생략 ...
                textView.isVisible = true

            }


        }
    }
fun getRandomNumber(): List<Int> {

        val numberList = mutableListOf<Int>()
            .apply {
                for (i in 1..30) {
                    //이미 뽑은 번호는 초기화 안하고 그냥 넘어감.
                    if (pickNumberSet.contains(i)) {
                        continue
                    }

                    this.add(i)
                }
            }

       ... 생략 ...

        //이미 뽑았던 번호 리스트 + 새로 뽑은 번호 리스트에서 이미 뽑은 번호 뺀 갯수.
        val newList = pickNumberSet.toList() + numberList.subList(0, 5 - pickNumberSet.size)

       ... 생략 ...
 }

 

번호를 직접 선택해서 추가하는 부분입니다. 이 부분에서 예외 처리가 필요합니다. return 부분을 보면, 토스트 메시지를 띄운 후에 @setOnClickListener 내의 메소드로 리턴하게 됩니다.

textView에는 직접 고른 번호 갯수 만큼이 할당되고, 고른 번호가 나타낼 수 있도록 합니다.

if (pickNumberSet.contains(numberPicker.value)) {
                Toast.makeText(this, "이미 선택한 번호입니다.", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
}

val textView = numberTextViewList[pickNumberSet.size]
...생략...
textView.text = numberPicker.value.toString()

pickNumberSet.add(numberPicker.value)

 

다음은 초기화 부분입니다. 버튼을 클릭 시, 골랐던 번호가 지워지도록 합니다. 그리고 기존에 저장된 numberTextViewList를 forEach 루프로 반복시켜, 요소들을 보이지 않도록 설정합니다(false 값).

fun initClearButton() {
        clearButton.setOnClickListener {
            pickNumberSet.clear()
            numberTextViewList.forEach {
                it.isVisible = false
            }

            ...생략...
        }

}

동그라미 배경은 리소스의 drawable 폴더에 생성한 후, 직접 배경을 만들고 가져온 것입니다.

이와 관련된 자세한 내용은 아래를 참고해주시기 바랍니다.

https://itknowledgeshare.tistory.com/46

 

밑그림 그리기 Soild, Stroke, Corner, Gradient

안드로이드 상에서 배경이 될 만한 밑그림 제작이 가능합니다. 디자이너로부터 밑그림 디자인 시안을 받는 것보다 직접 그려서 진행하는 부분이 좋습니다. 그 이유는 디자인 시안의 경우, 깨지

itknowledgeshare.tistory.com

 

<최종 화면>

반응형
댓글