我們用幾個簡單的textView來顯示從api撈回來的資料,包括商品名、商品描述、價錢及數量,並藉由輸入editText來更改viewModel中資料的值,進而讓其他訂閱viewModel的textView來更新資料。
1.添加依賴 lifecycle_version目前為2.2.0
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
2.寫一個類別,模擬取得API資料的部分
interface IProductAPI {
interface LoadAPICallBack {
fun onGetResult(productResponse: ProductResponse)
}
fun getProduct(productId:String, loadAPICallBack: LoadAPICallBack)
}
class ProductAPI: IProductAPI {
override fun getProduct(productId:String, loadAPICallBack: IProductAPI.LoadAPICallBack) {
//模擬從API取得資料
val handler = Handler()
handler.postDelayed(Runnable {
val productResponse = ProductResponse()
productResponse.id = "pixel3"
productResponse.name = "Google Pixel 3"
productResponse.price = 27000
loadAPICallBack.onGetResult(productResponse)
}, 1000)
}
}
2.寫Repository,來解析與處理資料。
interface IProductRepository {
fun getProduct(productId: String, loadProductCallback: LoadProductCallback)
fun buy(id: String, items: Int, callback: BuyProductCallback)
interface LoadProductCallback {
fun onProductResult(productResponse: ProductResponse)
}
interface BuyProductCallback {
fun onBuyResult(isSuccess: Boolean)
}
}
class ProductRepository(private val productAPI: IProductAPI) : IProductRepository {
override fun buy(id: String, items: Int, callback: IProductRepository.BuyProductCallback) {
//模擬購買成功
callback.onBuyResult(true)
}
override fun getProduct(productId: String, loadProductCallback: IProductRepository.LoadProductCallback) {
productAPI.getProduct(productId, object : IProductAPI.LoadAPICallBack {
override fun onGetResult(productResponse: ProductResponse) {
loadProductCallback.onProductResult(productResponse)
}
})
}
}
3.此時就是MVVM和MVP最不同的地方,ViewModel並不使用callback的方式來通知View,而是用Observer pattern的概念,由View來訂閱(subscribe)ViewModel中它要的資料,並在資料異動時才更新UI,因此,MVVM都會搭配如Data Binding等library來實現Observer pattern。
class ProductViewModel(private val productRepository: IProductRepository) :ViewModel(){
//將變數設為LiveData型態
val productName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
val productPrice: MutableLiveData<Int> by lazy {
MutableLiveData<Int>()
}
val productCount: MutableLiveData<Int> by lazy {
MutableLiveData<Int>()
}
fun getProduct(productId: String) {
productRepository.getProduct(productId, object : IProductRepository.LoadProductCallback {
override fun onProductResult(productResponse: ProductResponse) {
//用setValue來改變值
productName.value = productResponse.name
productDesc.value = productResponse.desc
productPrice.value = productResponse.price
}
})
}
fun updateProduct(name:String,price:Int,count:Int){
productName.value = name
productPrice.value = price
productCount.value = count
}
}
4.在activity處理dataBinding與實例化repository還有viewModel
class ProductActivity : AppCompatActivity() {
private val productId = "pixel3"
private lateinit var model:ProductViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_product)
val productAPI = ProductAPI()
val productRepository = ProductRepository(productAPI)
model = ProductViewModel(productRepository)
model.getProduct(productId)
val observerName = Observer<String>{ newName->
tv_name.text = newName
}
val observerPrice = Observer<Int>{ newName->
tv_price.text = newName.toString()
}
val observerCount = Observer<Int>{ newName->
tv_counts.text = newName.toString()
}
//第一個參數為lifecycle的owner
model.productName.observe(this,observerName)
model.productPrice.observe(this,observerPrice)
model.productCount.observe(this,observerCount)
btn_update.setOnClickListener{
model.updateProduct(
et_name.text.toString(),
et_price.text.toString().toInt(),
et_count.text.toString().toInt())
}
}
}
5.layout,無特別的地方。
<LinearLayout 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"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="36sp" />
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:textSize="24sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="數量:"
android:textSize="24sp" />
<TextView
android:id="@+id/tv_counts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="12dp"
android:textSize="24sp" />
</LinearLayout>
<EditText
android:id="@+id/et_name"
android:layout_width="200dp"
android:layout_height="50dp"
android:hint="品名" />
<EditText
android:id="@+id/et_price"
android:layout_width="200dp"
android:layout_height="50dp"
android:hint="價格" />
<EditText
android:id="@+id/et_count"
android:layout_width="200dp"
android:layout_height="50dp"
android:hint="數量" />
<Button
android:id="@+id/btn_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="24dp"
android:padding="10dp"
android:text="更新數值" />
</LinearLayout>