View에 필요한 데이터를 분리
View에 어떤 데이터가 필요한지 알기 위해 디자인을 먼저 살펴봅니다.
xml이든 디자이너가 넘겨준 제플린, 이미지 파일이든 상관 없습니다.
- 수식
- 계산 결과
둘 모두 하나의 TextView에 보여질 예정이므로 String LiveData를 하나 준비합니다.
private val _text = MutableLiveData("") val text: LiveData<String> get() = _text
수식은 계속해서 더해지고 지워져야 하므로 저장할 변수가 필요합니다.
계산 결과는 계산이 완료되었을 때 바로 String LiveData로 보여주면 되므로 저장할 변수가 필요하지 않습니다.
private var expression = Expression.EMPTY
View에서 ViewModel에 전달할 이벤트
레이아웃 xml 또는 Activity, Fragment에서 ViewModel의 함수를 호출하여 유저가 발생시킨 이벤트를 전달해야 합니다.
class MainViewModel : ViewModel() { fun addToExpression(operand: Int) { ... } fun addToExpression(operator: Operator) { ... } fun removeLast() { ... } fun calculate() { ... } }
xml에서 데이터바인딩 처리
<layout> <data> <import type="edu.nextstep.camp.calculator.domain.Operator" /> <variable name="viewModel" type="edu.nextstep.camp.calculator.MainViewModel" /> </data> <androidx.constraintlayout.widget.ConstraintLayout> <TextView android:id="@+id/textView" android:text="@{viewModel.text}" /> <com.google.android.material.button.MaterialButton android:id="@+id/buttonDelete" android:onClick="@{() -> viewModel.removeLast()}" /> <GridLayout> <Button android:id="@+id/button0" android:onClick="@{() -> viewModel.addToExpression(0)}" /> <!-- 1 ~ 9 버튼 생략 --> <Button android:id="@+id/buttonPlus" android:onClick="@{() -> viewModel.addToExpression(Operator.Plus)}" /> <!-- Minus, Multiply, Divide 생략 --> <Button android:id="@+id/buttonEquals" android:onClick="@{() -> viewModel.calculate()}" /> </GridLayout> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
Activity에서 데이터바인딩 클래스 사용
데이터바인딩을 통해 레이아웃 xml 파일을 토대로 XxxBinding 클래스가 자동 생성됩니다.
여기서는 activity_main.xml 파일에 해당하는 ActivityMainBinding 클래스가 해당됩니다.
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val viewModel: MainViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.lifecycleOwner = this binding.viewModel = viewModel } }
ViewModel에서 발생한 이벤트를 View에서 처리
=
버튼을 눌렀을 때 수식이 완성되어 있으면 계산이 수행됩니다.
하지만 수식이 미완성 상태이면 ViewModel에서 계산을 할 수 없다는 이벤트를 View에 전달하여 View에서 사용자에게 에러가 발생했음을 알려주어야 합니다.
class MainViewModel : ViewModel() { private val _onCalculationErrorEvent = MutableLiveData<Unit>() val onCalculationErrorEvent: LiveData<Unit> get() = _onCalculationErrorEvent } class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { // ... viewModel.onCalculationErrorEvent.observe(this) { Toast.makeText(this, R.string.incomplete_expression, Toast.LENGTH_SHORT).show() } } }