
인스턴스 상태 저장 메커니즘을 사용하여 데이터를 저장하지 않을 경우 해당 데이터가 소멸되는데 ViewModel은 데이터 지속성을 위한 편리한 API를 제공하여 이 문제를 해결한다고한다.


<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:paddingHorizontal="10dp"
>
<TextView
android:id="@+id/number_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/number_input_edittext"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="number"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintEnd_toStartOf="@+id/plus_btn"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/number_textview"
android:layout_marginTop="30dp"
/>
<Button
android:id="@+id/plus_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="더하기"
app:layout_constraintEnd_toStartOf="@+id/minus_btn"
app:layout_constraintStart_toEndOf="@+id/number_input_edittext"
app:layout_constraintTop_toTopOf="@+id/number_input_edittext"
android:layout_marginHorizontal="10dp"
/>
<Button
android:id="@+id/minus_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="빼기"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/plus_btn"
app:layout_constraintTop_toTopOf="@+id/number_input_edittext" />
</androidx.constraintlayout.widget.ConstraintLayout>
enum class ActionType {
PLUS, MINUS
}
//데이터의 변경
// 뷰모델은 데이터의 변경사항을 아렬주는 라이브 데이터를 가지고있다.
class MyNumberViewModel(
private val coroutineScope: CoroutineScope =
//SupervisorJob(): SupervisorJob은 부모-자식 관계의 코루틴 설정
// 만약 자식 코루틴 중 하나가 실패시, 다른 자식 코루틴은 영향을 받지 않고 계속 실행.
CoroutineScope((SupervisorJob() + Dispatchers.Main.immediate))
) : ViewModel() {
//mutablelivedata - 수정 가능
//livedata - 값 변경X
//내부에서 설정하는 변경가능한 자료형은 Mutable로
private val _currentValue = MutableLiveData<Int>()
//변경되지 않는 데이터를 가져올 때 이름을 _ 언더스코어 없이 설정
// 공개적으로 가져오는 변수는 private가 아닌 퍼블릭으로 외부에서도 접근 가능하도록 설정
// 하지만 값을 직접 라이브 데ㅣㅇ터에 접근하지 않고 뷰모델을 통해 가져올 수 있도록 설정
val currentValue: LiveData<Int>
get() = _currentValue
//사용자의 입력값
private val _currentUserInput = MutableLiveData<String>()
val currentUserData : LiveData<String>
get() = _currentUserInput
//ViewModel이 시작될 때 초기값 설정
init {
Log.d("ViewModel", "nyh 생성자 호출")
_currentValue.value = 0
}
fun updateValue(actionType: ActionType, input: Int) {
when (actionType) {
ActionType.PLUS ->
_currentValue.value = _currentValue.value?.plus(input)
ActionType.MINUS ->
_currentValue.value = _currentValue.value?.minus(input)
}
this._currentUserInput.value = ""
}
override fun onCleared() {
coroutineScope.cancel()
}
}
class MainActivity : AppCompatActivity(), View.OnClickListener {
lateinit var myNumberViewModel: MyNumberViewModel
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
myNumberViewModel = ViewModelProvider(this).get(MyNumberViewModel::class.java)
myNumberViewModel.currentValue.observe(this, Observer {
Log.d("Activity", "nyh 라이브 데이터 값 변경 $it")
binding.numberTextview.text = it.toString()
})
myNumberViewModel.currentUserData.observe(this, Observer { changeUserInput ->
binding.numberInputEdittext.setText(changeUserInput)
Log.d("Activity", "nyh 라이브 데이터 값 변경 $changeUserInput")
})
binding.plusBtn.setOnClickListener(this)
binding.minusBtn.setOnClickListener(this)
}
//클릭
override fun onClick(view: View?) {
// 비동기 작업을 lifecycleScope에서 실행
lifecycleScope.launch {
val userInputString = binding.numberInputEdittext.text.toString()
if (userInputString.isEmpty()) {
return@launch
}
val userInputNumber =
binding.numberInputEdittext.text.toString().toInt()
when (view) {
binding.plusBtn ->
myNumberViewModel.updateValue(actionType = ActionType.PLUS, userInputNumber)
binding.minusBtn ->
myNumberViewModel.updateValue(actionType = ActionType.MINUS, userInputNumber)
}
}
}
}
정리가 잘되어있어서 보기 편리했어요!