Kotlin

MVVM 강의 실습

내손은개발 🐾 2024. 4. 16. 20:50

단순 MVVM 패턴을 강의를 통해 진행한 내용 따라 써서 실습해보기

 

예제 1

버튼을 눌렀을 때 text의 값이 변경되도록 실습

 

 

textView의 id = tv_test

button의 id = btn_test

activity_main 코드

더보기
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".main.MainActivity">


    <TextView
        android:id="@+id/tv_test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:gravity="center"
        android:textSize="30sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="textView" />

    <Button
        android:id="@+id/btn_test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        app:layout_constraintTop_toBottomOf="@id/tv_test" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

1. ViewMode 생성

먼저 mainActivity의 viewModel을 생성

ViewModel()을 상속받게 생성하면 된다.

MainViewModel.kt

 

 

 

2.mainActivity

ViewModel 인스턴스 생성하기

 

2가지 방법으로 불러올 수 있다.

 

 val viewModel = ViewModelProvider(this)[MainViewModel::class.java] //[1]
 
 private val viewModel: MainViewModel by viewModels() //[2]

[1]

this가 사용된 경우는 Activity, Fragment 인스턴스를 의미한다.

ViewModel이 Activity나 Fragment의 수명주기와 연결되로고 하는데 도움이 된다.

 

[2]

by viewModels()를 사용해서 ViewModelProvider의 인스턴스를 사용하여 ViewModel을 생성하고 반환한다.

사용하기 위해서는 activity-ktx, fragment-ktx를 build.gradle에서 받아야한다.

 

 

3. lazy 사용해서 TextView, Button 초기화

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    private val viewModel: MainViewModel by viewModels()

    private val tvTest: TextView by lazy { //[1]
        binding.tvTest
    }

    private val btnText: Button by lazy { //[2]
        binding.btnTest
    }
    
    ...

 

 

 

4. 버튼 Click Event

    private fun initView() {
        btnText.setOnClickListener {
    //            tvTest.text = "hi" //[1]
            viewModel.onClickTest("hi") //[2]
        }
    }

[1]

버튼을 click했을 경우 hi를 즉각적으로 TextView 변경

[2]

viewModel의 onClickTest 메서드를 호출하여서 결과를 LiveData를 통해 Activity에 전달한다.

class MainViewModel : ViewModel() {

    private val _event: MutableLiveData<String> = MutableLiveData()
    val event: LiveData<String> get() = _event

    fun onClickTest(action: String) {
        val result = "result $action" //[2]
        _event.value = result
    }
}

▶LiveData ?

ViewModel이 View에 데이터를 줄 때 사용한다.

식별 가능한 데이터 홀더 클래스이다. 스스로 수명주기를 인식한다.

수명주기를 인식하여서 Activity나 Fragment가 활성 상태일 때만 데이터를 전달하고 비활성 상태일 때는 업데이트를 건너뛰어 리소스를 효율적으로 사용한다.

 

 

 

예제 2

입력받은 데이터를 Toast메시지 띄우기

 

et_name

et_id

et_password

btn_sign_up

더보기
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".signup.SignUpActivity">

    <EditText
        android:id="@+id/et_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:hint="name"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/et_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:hint="id"
        app:layout_constraintTop_toBottomOf="@id/et_name" />

    <EditText
        android:id="@+id/et_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:hint="password"
        app:layout_constraintTop_toBottomOf="@id/et_id" />

    <Button
        android:id="@+id/btn_sign_up"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:text="회원가입"
        app:layout_constraintTop_toBottomOf="@id/et_password" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

 

위에 설명한 내용은 생략하고 정리를 해보면

 

1. SignUpViewModel - 생성

2. MainActivity

class SignUpActivity : AppCompatActivity() {

    private val viewModel: SignUpViewModel by viewModels()

    private val etName: EditText by lazy {
        findViewById(R.id.et_name)
    }

    private val etId: EditText by lazy {
        findViewById(R.id.et_id)
    }

    private val etPassword: EditText by lazy {
        findViewById(R.id.et_password)
    }

    private val btnSignUp: Button by lazy {
        findViewById(R.id.btn_sign_up)
    }
    
   ...

 

    private fun initView() {
        btnSignUp.setOnClickListener {
            viewModel.onClickSignUp(
                etName.text.toString(),
                etId.text.toString(),
                etPassword.text.toString()
            )
        }
    }
    private fun initViewModel() = with(viewModel) {
        event.observe(this@SignUpActivity) { event ->
            when (event) {
                is SignUpEvent.SignUpSuccess -> {
                    Toast.makeText(this@SignUpActivity,"$event",Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

observe ?

LiveData의 해당 LiveData가 변경될 때 수행할 작업을 정의하는 메서드이다. LiveData의 값이 변경될 때마다 Observe가 호출되어 새 값으로 UI를 업데이트하거나 다른 작업을 수행할 수 있다.

 

 

3.ViewModel

class SignUpViewModel : ViewModel() {

    private val _event: MutableLiveData<SignUpEvent> = MutableLiveData()
    val event: LiveData<SignUpEvent> get() = _event

    fun onClickSignUp(name: String, id: String, password: String) {
        _event.value = SignUpEvent.SignUpSuccess(
            name,
            id,
            password
        )
    }
}

예제1과 다르게 name, id, password를 받아와서 LiveData를 통해 전달한다.

SignUpEvent라는 이벤트를 정의하는 클래스를 따로 생성해줬다.

 

4.Event

sealed interface SignUpEvent {
    data class SignUpSuccess(
        val name: String,
        val id: String,
        val password: String
    ) : SignUpEvent
}

 

 

 

 

 

 

 

'Kotlin' 카테고리의 다른 글

Kotlin - 입력 예외처리  (1) 2024.03.14