📱【Android】MVVM 完整架構範例 基礎寫法與基礎用法💻
MVVM (Model-View-ViewModel) 是一種軟件設計模式,主要用於在 Android 應用開發中。它是一種架構模式,分離了應用程序的界面(視圖)和業務邏輯(模型),並通過 ViewModel 將它們連接在一起。這樣,開發人員可以更加輕松地管理應用程序的代碼,並提高代碼的可維護性和可測試性。
文章目錄
- 導入Library
- 創建RetrofitUtil
- API Service & data class
- Model層(專心取得數據)
- ViewModel層(專心取得Model層的數據並且取得UI所需資料)
- View層(與VM層綁定 達到數據變動就更新UI)
- 使用DataBinding XML宣告ViewModel
- 效果展示
1.導入Library
DataBinding
buildFeatures {
dataBinding true
}
Retrofit & GsonCovert & Coroutine
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
AndroidManifest網路權限
<uses-permission android:name="android.permission.INTERNET"/>
2.創建RetrofitUtil
class RetrofitUtil {
private val retrofit = Retrofit.Builder()
.baseUrl("https://gank.io/api/")
.addConverterFactory(GsonConverterFactory.create())
.build()
companion object {
val instance : RetrofitUtil by lazy { RetrofitUtil() }
}
fun <T> getService(clazz: Class<T>) = retrofit.create(clazz)
}
3.API Service & data class
API Service
interface GirlService {
@GET("v2/data/category/Girl/type/Girl/page/1/count/10")
suspend fun getGirl() : HttpResult
}
data class
data class HttpResult(val data: List<Girl>)
data class Girl(val images: List<String>, val title: String)
4.Model層(專心取得數據)
class PersonModel {
interface OnDataReadyCallback {
fun onDataReady(result: HttpResult)
}
fun changeData(callback: OnDataReadyCallback) {
CoroutineScope(Dispatchers.IO).launch {
val girl = RetrofitUtil.instance
.getService(GirlService::class.java)
.getGirl()
withContext(Dispatchers.Main) {
callback.onDataReady(girl)
}
}
}
}
5.ViewModel層(專心取得Model層的數據並且取得UI所需資料)
class UserViewModel : ViewModel() {
private val personModel = PersonModel()
var account = MutableLiveData<String>()
val password = MutableLiveData<String>()
fun getData() {
personModel.changeData(object : PersonModel.OnDataReadyCallback {
override fun onDataReady(result: HttpResult) {
account.value = result.data[0].title
password.value = result.data[1].title
}
})
}
}
6.View層(與VM層綁定 達到數據變動就更新UI)
class MainActivity : AppCompatActivity() {
lateinit var userViewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
userViewModel =
ViewModelProvider.AndroidViewModelFactory(application).create(UserViewModel::class.java)
binding.viewModel = userViewModel
binding.lifecycleOwner = this
}
fun login(view: View) {
Toast.makeText(
this,
"{userViewModel.account.value} \n{userViewModel.password.value}",
Toast.LENGTH_SHORT
).show()
}
fun getData(view: View) {
userViewModel.getData()
}
}
7.使用DataBinding XML宣告ViewModel
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="viewModel"
type="fly.com.mvvm.UserViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:onClick="login"
android:text="登入"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/passwordEdit" />
<EditText
android:id="@+id/accountEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:hint="請輸入帳號"
android:text="@={viewModel.account}"
app:layout_constraintEnd_toEndOf="@+id/button"
app:layout_constraintStart_toStartOf="@+id/button"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/passwordEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:hint="請輸入密碼"
android:text="@={viewModel.password}"
app:layout_constraintEnd_toEndOf="@+id/button"
app:layout_constraintHorizontal_bias="0.504"
app:layout_constraintStart_toStartOf="@+id/button"
app:layout_constraintTop_toBottomOf="@+id/accountEdit" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@{viewModel.account}"
app:layout_constraintStart_toStartOf="@+id/passwordEdit"
app:layout_constraintTop_toBottomOf="@+id/button" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@{viewModel.password}"
app:layout_constraintEnd_toEndOf="@+id/passwordEdit"
app:layout_constraintTop_toBottomOf="@+id/button" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="取資料"
android:onClick="getData"
app:layout_constraintEnd_toEndOf="@+id/textView3"
app:layout_constraintStart_toStartOf="@+id/textView2"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
8.效果展示