Page tree
Skip to end of metadata
Go to start of metadata

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()
        }
    }
}


  • No labels