本篇會延續 Day7 MVVM專案-1 接著修改
今天會將result改為MediatorLiveData
MediatorLiveData簡而言之就是能同時觀察數個livedata
以result為例 原本是按plus後對left與right的數值相加
改成MediatorLiveData以後
left 或right數據變更時會即時通知result
調整result
先將val result = MutableLiveData("")
改為以下內容
Stage01ViewModel.kt
val result = MediatorLiveData<String>().apply {
value = ""
val plus: (String?) -> Unit = plus@{
val leftNum = left.value?.toIntOrNull()
if (leftNum == null) {
visibleResult.value = false
return@plus
}
val rightNum = right.value?.toIntOrNull()
if (rightNum == null) {
visibleResult.value = false
return@plus
}
value = (leftNum + rightNum).toString()
visibleResult.value = true
}
addSource(left, plus)
addSource(right, plus)
}
addSource(觀察數據, 行為)
val plus這段的意思是當數據變更時 檢查left 或right 數值
如果其中一個非int或是為null 則改result的可見度為隱藏 並且return
否則相加 並且result的可見度為可見
這段code其實是把以下這兩段code給整合了
Stage01ViewModel.kt
fun plus() {
val leftNum =
left.value.toString().toIntOrNull() ?: run { visibleResult.value = false; return }
val rightNum =
right.value.toString().toIntOrNull() ?: run { visibleResult.value = false; return }
result.value = (leftNum + rightNum).toString()
visibleResult.value = true
}
Stage01Activity.kt
override fun onCreate(savedInstanceState: Bundle?) {
val setInvisibleResult = Observer<String?> { viewModel.visibleResult.value = false }
viewModel.left.observe(this, setInvisibleResult)
viewModel.right.observe(this, setInvisibleResult)
}
因為數據會即時判斷了
所以首頁針對left right的observe也可以移除了
在補上今天的測試案例
Stage01ViewModelTest.kt
@Test
fun one_plus_one_is_two() {
// given
val givenVal = "1"
// when
Espresso.onView(ViewMatchers.withId(R.id.et_left)).perform(ViewActions.typeText(givenVal))
Espresso.onView(ViewMatchers.withId(R.id.et_right)).perform(ViewActions.typeText(givenVal))
// then
val expectedText = String.format(rule.activity.getString(R.string.stage01_result_format),givenVal, givenVal, "2")
Espresso.onView(ViewMatchers.withId(R.id.tv_result)).check(
ViewAssertions.matches(
ViewMatchers.withText(expectedText)
)
)
}
然後運行時會發現之前寫的測試失敗了
原因是因為首頁的observe被拿掉了
現在數據變更時result的結果會根據fun plus() 即時顯示
而不是輸入數據時不管結果如何都先隱藏
所以result_should_be_invisible_when_input()也要做對應的調整
附上修正後的測試案例
@Before
fun reset() {
Espresso.onView(ViewMatchers.withId(R.id.et_left)).perform(ViewActions.replaceText(""))
Espresso.onView(ViewMatchers.withId(R.id.et_right)).perform(ViewActions.replaceText(""))
}
@Test
fun result_should_be_invisible_when_input() {
// given
plus() // result view is visible
Espresso.onView(ViewMatchers.withId(R.id.tv_result)).check(ViewAssertionsEx.isVisibility())
// when
Espresso.onView(ViewMatchers.withId(R.id.et_left)).perform(ViewActions.replaceText(""))
// then
Espresso.onView(ViewMatchers.withId(R.id.tv_result)).check(ViewAssertionsEx.isInvisible())
}
最後附上今天的solution
https://github.com/mars1120/jetpackMvvmDemo/tree/mvvm-stage01a