1. 뷰바인딩 사용설정

android {
...
viewBinding {
        enable = true
    }
}

 

2. 화면 설계

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/editTextName"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="이름"
                app:endIconMode="clear_text"
                app:errorTextAppearance="@style/TextAppearance.AppCompat.Large">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:singleLine="true"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/editTextAge"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:endIconMode="clear_text"
                app:errorTextAppearance="@style/TextAppearance.AppCompat.Large">

                <com.google.android.material.textfield.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="나이"
                    android:inputType="number"
                    android:singleLine="true"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <TextView
                android:id="@+id/type"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="15dp"
                android:gravity="center"
                android:text="종류"
                android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            <RadioGroup
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <RadioButton
                    android:id="@+id/radioButtonTypeDog"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="30dp"
                    android:layout_marginEnd="30dp"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:text="강아지" />

                <RadioButton
                    android:id="@+id/radioButtonTypeCat"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="30dp"
                    android:layout_marginEnd="30dp"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:text="고양이" />

                <RadioButton
                    android:id="@+id/radioButtonTypeParrot"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="30dp"
                    android:layout_marginEnd="30dp"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:text="앵무새" />

            </RadioGroup>

            <TextView
                android:id="@+id/gender"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="15dp"
                android:gravity="center"
                android:text="성별"
                android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            <RadioGroup
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <RadioButton
                    android:id="@+id/radioButtonGenderMale"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="60dp"
                    android:layout_marginEnd="60dp"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:text="수컷" />

                <RadioButton
                    android:id="@+id/radioButtonGenderFemale"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="60dp"
                    android:layout_marginEnd="60dp"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:text="암컷" />

            </RadioGroup>

            <TextView
                android:id="@+id/gender2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="15dp"
                android:gravity="center"
                android:text="좋아하는 간식"
                android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            <com.google.android.material.checkbox.MaterialCheckBox
                android:id="@+id/checkBoxAll"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="간식전체"
                app:checkedState="unchecked" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="20dp"
                android:orientation="vertical">

                <CheckBox
                    android:id="@+id/checkBoxApple"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="사과" />

                <CheckBox
                    android:id="@+id/checkBoxBanana"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="바나나" />

                <CheckBox
                    android:id="@+id/checkBoxDaeChu"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="대추" />

            </LinearLayout>

            <Button
                android:id="@+id/buttonSave"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="저장" />

            <Button
                android:id="@+id/buttonPrint"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="출력" />

            <LinearLayout
                android:id="@+id/animalContainer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
            </LinearLayout>

        </LinearLayout>
    </ScrollView>

</LinearLayout>

 

3. AnimalClass 설계

class AnimalClass(val name:String,val age:Int,val type:String,val gender:String,val snackList:MutableList<String>) {

    
}

 

4. Main_Activity 설계

import android.content.Context
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.google.android.material.checkbox.MaterialCheckBox
import com.lion.a017ex_animalmanager.databinding.ActivityMainBinding
import android.widget.CompoundButton.OnCheckedChangeListener
import android.view.inputmethod.InputMethodManager
import android.widget.TextView

class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding

    var animalList = mutableListOf<AnimalClass>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding.root)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }


        activityMainBinding.apply {
            //기본 체크값 세팅
            radioButtonTypeDog.isChecked = true
            radioButtonGenderMale.isChecked = true
            checkBoxAll.isChecked = true
            checkBoxApple.isChecked = true
            checkBoxBanana.isChecked = true
            checkBoxDaeChu.isChecked = true

            buttonSave.setOnClickListener {
                save()
                resetInput()
            }
            buttonPrint.setOnClickListener {
                print()
                resetInput()
            }


            checkBoxAll.addOnCheckedStateChangedListener { checkBox, state ->

                when (state) {
                    // 체크 상태일 때
                    MaterialCheckBox.STATE_CHECKED -> {
                        // 모든 체크박스를 체크한다.
                        checkBoxApple.isChecked = true
                        checkBoxBanana.isChecked = true
                        checkBoxDaeChu.isChecked = true

                    }
                    // 체크 상태가 아닐 때
                    MaterialCheckBox.STATE_UNCHECKED -> {
                        // 모든 체크스를 체크 해제한다.
                        checkBoxApple.isChecked = false
                        checkBoxBanana.isChecked = false
                        checkBoxDaeChu.isChecked = false
                    }
                }
            }

            val checkBoxListener = OnCheckedChangeListener { buttonView, isChecked ->
                // 체크되어 있는 체크박스의 개수를 담을 변수
                var checkedCount = 0

                // 체크박스들을 검사한다.
                if (checkBoxApple.isChecked) {
                    checkedCount++
                }
                if (checkBoxBanana.isChecked) {
                    checkedCount++
                }
                if (checkBoxDaeChu.isChecked) {
                    checkedCount++
                }

                // 체크박스 개수에 따라 상태를 설정한다.
                checkBoxAll.checkedState = if (checkedCount == 0) {
                    MaterialCheckBox.STATE_UNCHECKED
                } else if (checkedCount == 3) {
                    MaterialCheckBox.STATE_CHECKED
                } else {
                    MaterialCheckBox.STATE_INDETERMINATE
                }
            }

            // 모든 체크박스에 설정한다
            checkBoxApple.setOnCheckedChangeListener(checkBoxListener)
            checkBoxBanana.setOnCheckedChangeListener(checkBoxListener)
            checkBoxDaeChu.setOnCheckedChangeListener(checkBoxListener)


        }
    }

    fun save() {
        //저장할때 사용할 변수
        var name: String?
        var age: Int?
        var type: String?
        var gender: String?
        var snackList = mutableListOf<String>()

        name = activityMainBinding.editTextName.editText?.text.toString()
        age = activityMainBinding.editTextAge.editText?.text.toString().toInt()

        //동물종류에따라 분기하여 저장한다
        if (activityMainBinding.radioButtonTypeDog.isChecked){
            type = "강아지"
        }else if(activityMainBinding.radioButtonTypeCat.isChecked){
            type = "고양이"
        }else {
            type = "앵무새"
        }

        //동물성별에 따라 분기하여 저장한다
        if (activityMainBinding.radioButtonGenderMale.isChecked){
            gender = "수컷"
        }else{
            gender = "암컷"
        }

        //동물이 좋아하는 간식을 리스트에담는다
        if (activityMainBinding.checkBoxApple.isChecked){
            snackList.add("사과")
        }
        if (activityMainBinding.checkBoxBanana.isChecked){
            snackList.add("바나나")
        }
        if (activityMainBinding.checkBoxDaeChu.isChecked){
            snackList.add("대추")
        }

        //저장된 정보를 토대로 동물클래스를 생성한다
        var animalClass = AnimalClass(name,age,type,gender,snackList)

        //전체동물리스트에 객체를 추가한다
        animalList.add(animalClass)

    }

    fun print() {
        activityMainBinding.apply {
            // 기존의 내용을 모두 지운다.
            animalContainer.removeAllViews()
            // animalList의 각 요소를 순회하며 출력
            animalList.forEach { animal ->
                // 동물 정보를 담을 TextView 생성
                val animalInfo = TextView(this@MainActivity).apply {
                    text = """
                이름: ${animal.name}
                타입: ${animal.type}
                나이: ${animal.age}
                성별: ${animal.gender}
                좋아하는 간식: ${animal.snackList.joinToString(", ")}
                ---------------------------
            """.trimIndent()
                    textSize = 16f
                }
                // animalContainer에 추가
                animalContainer.addView(animalInfo)
            }
        }


    }

    // 입력 요소 초기화
    fun resetInput(){
        activityMainBinding.apply {
            // 각 입력 요소를 비워준다.
            activityMainBinding.editTextName.editText?.setText("")
            activityMainBinding.editTextAge.editText?.setText("")

            // 첫 번째 입력 요소에 포커스를 준다.
            activityMainBinding.editTextName.editText?.requestFocus()

            // 키보드를 내린다.
            val imm = activityMainBinding.root.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(activityMainBinding.root.windowToken, 0)
        }
    }
}

 

5. 동작영상

 

 

6. 출력부분에서 막힌부분

출력을 리스트뷰를 이용해서 쭉 출력하는 쉬운 방법이 있었지만 아직 부트캠프 진도상 리스트뷰를 배우지않아서 리스트뷰없이 출력하는법을 고민하다 동물리스트의 동물정보객체마다 스크롤뷰에 텍스트뷰를 추가하는방식을 사용했다

 

해당부분 코드는 다음과 같다

fun print() {
        //동물 리스트가 비어있다면
        if (animalList.size == 0){
            activityMainBinding.apply {
                // 기존의 내용을 모두 지운다.
                animalContainer.removeAllViews()
                val animalInfo = TextView(this@MainActivity).apply {
                    text = """
                저장된 동물없음
                ---------------------------
            """.trimIndent()
                    textSize = 16f
                }
                // animalContainer에 추가
                animalContainer.addView(animalInfo)
            }
        }else{
            activityMainBinding.apply {
                // 기존의 내용을 모두 지운다.
                animalContainer.removeAllViews()
                // animalList의 각 요소를 순회하며 출력
                animalList.forEach { animal ->
                    // 동물 정보를 담을 TextView 생성
                    val animalInfo = TextView(this@MainActivity).apply {
                        text = """
                이름: ${animal.name}
                타입: ${animal.type}
                나이: ${animal.age}
                성별: ${animal.gender}
                좋아하는 간식: ${animal.snackList.joinToString(", ")}
                ---------------------------
            """.trimIndent()
                        textSize = 16f
                    }
                    // animalContainer에 추가
                    animalContainer.addView(animalInfo)
                }
            }
        }
    }