Android

📱【Android】MVVM 完整架構範例 基礎寫法與基礎用法💻

📱【Android】MVVM 完整架構範例 基礎寫法與基礎用法💻

MVVM (Model-View-ViewModel) 是一種軟件設計模式,主要用於在 Android 應用開發中。它是一種架構模式,分離了應用程序的界面(視圖)和業務邏輯(模型),並通過 ViewModel 將它們連接在一起。這樣,開發人員可以更加輕松地管理應用程序的代碼,並提高代碼的可維護性和可測試性。


文章目錄

  1. 導入Library
  2. 創建RetrofitUtil
  3. API Service & data class
  4. Model層(專心取得數據)
  5. ViewModel層(專心取得Model層的數據並且取得UI所需資料)
  6. View層(與VM層綁定 達到數據變動就更新UI)
  7. 使用DataBinding XML宣告ViewModel
  8. 效果展示

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.效果展示

發表迴響