iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 7
0

介紹完ViewModel & LiveData 以及databinding以後
接下來可以開始構築專案了

今天的進度會從branch - databinding-2接續往下改

頁面的功能是輸入兩個數字 加總後顯示

xml布局

https://ithelp.ithome.com.tw/upload/images/20190922/20120279mEHwC6Vh6i.png

布局只貼重點 全貼太長了

activity_stage01.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android">
   ...
    <data>

        <variable
                name="viewModel"
                type="com.ithome11.jetpackmvvmdemo.main.s01.Stage01ViewModel" />
        <import type="android.view.View" />
    </data>
    ...

            <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/et_left"
                    android:text="@={viewModel.left}" />
      ...
            <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/et_right"
                    android:text="@={viewModel.right}"/>

        <androidx.appcompat.widget.AppCompatButton
                android:id="@+id/bt_clear"
                android:onClick="@{_ -> viewModel.clear()}"
               />


        <androidx.appcompat.widget.AppCompatButton
                android:id="@+id/bt_plus"
                android:onClick="@{_ -> viewModel.plus()}"
                 />

        <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/tv_result"
                android:text="@{@string/stage01_result_format(viewModel.left,
                viewModel.right, viewModel.result)}"
                android:visibility="@{viewModel.visibleResult ? View.VISIBLE : View.INVISIBLE}"
                />
...

</layout>

strings.xml

<resources>
   ...
    <string name="stage01_result_format">%s + %s = %s</string>
</resources>

其中 <import type="android.view.View" / > 為 android:visibility用的
如果不想用這種寫法的話也可以換成下敘寫法

<data>
    <variable
        name="viewModel"
        type="...."/>
</data>


<View
 android:layout_width="10dp"
 android:layout_height="10dp"
 android:visibility="@{viewModel.saleVisibility, default=gone}"/>

viewModel

@BindingAdapter("android:visibility")
fun setVisibility(view: View,  value : Boolean) {
     view.visibility = if (value) View.VISIBLE else View.GONE
}

ViewModel代碼
Stage01ViewModel.kt

class Stage01ViewModel : ViewModel() {
    val left = MutableLiveData<String>("")
    val right = MutableLiveData<String>("")
    val result = MutableLiveData<String>("")
    //tv_result根據visibleResult決定是否顯示
val visibleResult = MutableLiveData<Boolean>().apply { value = false }

    fun plus() {
      //如果輸入的數字不能轉為int或null 則tv_result隱藏 並且不繼續往下執行
        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
    }
    //重設資料
    fun clear() {
        left.value = ""
        right.value = ""
        result.value = ""
        visibleResult.value = false
    }
}

如果只是要給預設值 那
MutableLiveData("") 與 MutableLiveData().apply { value = false }
兩種寫法都可以

接著去更新activity

Stage01Activity.kt

class Stage01Activity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_stage01)

        val viewModel = ViewModelProviders.of(this).get(Stage01ViewModel::class.java)

        DataBindingUtil.setContentView<ActivityStage01Binding>(this,
        R.layout.activity_stage01).let {
            it.setLifecycleOwner(this)
            it.viewModel = viewModel
        }

        val setInvisibleResult = Observer<String?> { viewModel.visibleResult.value = false }
        viewModel.left.observe(this, setInvisibleResult)
        viewModel.right.observe(this, setInvisibleResult)
    }

這裡針對left 跟right做觀察
當數據變化時 隱藏result
僅有點選plus時會更新與顯示數據

接著今天專案的基本功能就完成了
可以運行來試看看

接著要來新增測試案例 可以手動新增 也可以反白fun名稱後點快捷建立
https://ithelp.ithome.com.tw/upload/images/20190922/201202793vOwlaZNNF.png

https://ithelp.ithome.com.tw/upload/images/20190922/20120279nZ3dSPaZJH.png

Stage01ViewModelTest.kt (androidTest)

import androidx.test.ext.junit.runners.AndroidJUnit4

@RunWith(AndroidJUnit4::class)
class Stage01ViewModelTest {

    @get:Rule
    val rule = ActivityTestRule<Stage01Activity>(Stage01Activity::class.java)

    private fun plus() {
        Espresso.onView(ViewMatchers.withId(R.id.et_left)).perform(ViewActions.typeText("1"))
        Espresso.onView(ViewMatchers.withId(R.id.et_right)).perform(ViewActions.typeText("1"))
        Espresso.onView(ViewMatchers.withId(R.id.bt_plus)).perform(ViewActions.click())
    }
    @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.typeText("22"))
        // then Espresso.onView(ViewMatchers.withId(R.id.tv_result)).check(ViewAssertionsEx.isInvisible())
    }
}
object ViewAssertionsEx {
    @JvmStatic
    fun isInvisible() = ViewAssertion { view, noViewFoundException ->
        if (noViewFoundException != null ) throw noViewFoundException
        Assert.assertEquals(View.INVISIBLE, view.visibility)
    }

    @JvmStatic
    fun isVisibility() = ViewAssertion { view, noViewFoundException ->
        if (noViewFoundException != null ) throw noViewFoundException
        Assert.assertEquals(View.VISIBLE, view.visibility)
    }
}

build.gradle (module:app)

dependencies {
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
}

這個測試案例是left輸入1 right輸入1 之後點選plus
接著驗證result是否有顯示
接著再點選left輸入別的數據後
驗證result是否有正常隱藏

突然想到忘記先介紹Espresso... 之後再找篇幅來補充好了

今天的代碼在此
https://github.com/mars1120/jetpackMvvmDemo/tree/mvvm-stage01


上一篇
Day6 dataBinding - 2
下一篇
Day8 Espresso
系列文
Android × CI/CD 如何用基本的MVVM專案實現 CI/CD 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言