Android用kotlin搭建MVVM框架(一),Kotlin在Android中实现MVVM框架入门教程(一)

马肤

温馨提示:这篇文章已超过467天没有更新,请注意相关的内容是否还可用!

摘要:本篇文章介绍了使用Kotlin语言在Android平台上搭建MVVM框架的方法和步骤。文章首先概述了MVVM框架的概念和优势,然后详细阐述了使用Kotlin实现MVVM的具体步骤,包括模型、视图和视图模型的构建。文章通过清晰的逻辑和简洁的语言,为读者提供了一个入门指南,帮助他们在Android开发中使用Kotlin搭建MVVM框架。

Android用kotlin搭建MVVM框架(一)

  • 什么是MVVM
  • 搭建MVVM框架

    什么是MVVM

    Android的项目框架,大家应该都不陌生吧。而目前的项目框架有MVC,MVP,MVVM,MVI等各式各样的框架,目前用的最多的就是MVP和MVVM。那什么是MVVM呢,MVVM又和MVP,MVC有什么区别呢。 首先我们得知道什么是MVVM。所谓的MVVM就是通过Model,View,ViewModel来组成的一个项目框架,Model就是我们的实体类数据层,View就是我们的UI,和各种各样的view,ViewModel就是一个连接Model和View的桥梁,用来关联view和model,并且进行通知view,形成我们的双向绑定。这里的通知是通过观察者模式来进行的,如果不了解观察者模式,可以去查一下,我就不做过多的解释了;MVVM和MVP已经MVC的区别,网上也有很多大神给出了很多中看大,这里我们也可以自行去百度查看。

    搭建MVVM框架

    了解了什么是mvvm后我们就可以进行搭建项目框架了。在搭建框架之前我们需要进行一些依赖的添加.这些依赖有我们的网络框架的依赖,也有一些弹窗,权限等的依赖,我都写好了注释,大家可以自行查看

    api 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0' //viewmodel
        api 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0'
        api 'androidx.appcompat:appcompat:1.6.0-alpha05'
        api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' //协程
        api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
        api 'androidx.core:core-ktx:1.8.0' //kt
        api 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
        implementation 'com.google.android.material:material:1.6.1'
        //retrofit http
        api 'com.squareup.okhttp3:logging-interceptor:3.6.0'
        api 'com.squareup.retrofit2:adapter-rxjava2:2.6.0'
        api 'com.squareup.retrofit2:adapter-rxjava:2.6.0'
        api 'com.squareup.okhttp3:okhttp:3.12.0'
        implementation("com.squareup.okhttp3:logging-interceptor")
        api 'com.squareup.retrofit2:retrofit:2.6.0'
        api 'com.squareup.retrofit2:converter-gson:2.6.0'
        api 'com.squareup.retrofit2:adapter-rxjava2:2.6.0'
        api 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.2'//rxlifecycler
        api 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.7'
        api 'com.github.bumptech.glide:glide:4.13.2'
        //room数据库
        api "androidx.room:room-runtime:2.4.2"
        kapt "androidx.room:room-compiler:2.4.2" // Kotlin 使用 kapt
        api "androidx.room:room-ktx:2.4.2"//Coroutines support for Room 协程操作库
        implementation 'org.jetbrains:annotations:15.0'
        //httpDns 优化
        implementation 'com.aliyun.ams:alicloud-android-httpdns:2.2.2'
        //加载中的dialog
        implementation 'com.wang.avi:library:2.1.3'
        implementation 'com.blankj:utilcodex:1.31.0'
        //toastutlis框架
        api 'com.github.getActivity:XToast:8.9'
        //屏幕适配
        implementation 'me.jessyan:autosize:1.2.1'
        // 权限请求框架
        api 'com.github.getActivity:XXPermissions:16.6'
        //弹窗
        api 'com.github.li-xiaojun:XPopup:2.9.19'
    

    构建好依赖以后,我们需要创建一个module,用来放我们的base,所以就给module命名为BaseModule

    Android用kotlin搭建MVVM框架(一),Kotlin在Android中实现MVVM框架入门教程(一) 第1张

    然后我们就可以创建我们的需要的Base了,我们在创建base之前我们需要先想一个问题。ViewModel是用来当做桥梁连接view和model的,那我们就先来封装我们的ViewModel。而封装ViewModel的时候我们需要注意一个很关键的问题,ViewModel是作为桥梁来连接的,并且他需要通过观察者模式来通知我们的VIew进行UI的更新,那么这里就涉及到了我们的网络请求。还有一个网络请求中的一个等待的弹窗,弹窗的代码我在这篇文章中写了你们自己去复制代码就好了https://blog.csdn.net/weixin_53760974/article/details/128954762?spm=1001.2014.3001.5502

    我们来封装一下我们的BaseViewModel

    package com.example.basemodel.base
    import android.app.Activity
    import android.app.Application
    import android.content.Intent
    import android.os.Bundle
    import android.util.Log
    import androidx.lifecycle.*
    import com.example.basemodel.base.BaseViewModel.Companion.ParameterField.BUNDLE
    import com.example.basemodel.base.BaseViewModel.Companion.ParameterField.CLASS
    import com.example.basemodel.base.BaseViewModel.Companion.ParameterField.REQEUST_DEFAULT
    import com.example.basemodel.base.BaseViewModel.Companion.ParameterField.REQUEST
    import com.kt.network.net.ExceptionHandle
    import com.kt.network.net.IBaseResponse
    import com.kt.network.net.ResponseThrowable
    import com.trello.rxlifecycle2.LifecycleProvider
    import kotlinx.coroutines.*
    import java.lang.ref.WeakReference
    /**
     * @author 浩楠
     *
     * @date 2023/6/4-14:28
     *
     *      _              _           _     _   ____  _             _ _
     *     / \   _ __   __| |_ __ ___ (_) __| | / ___|| |_ _   _  __| (_) ___
     *    / _ \ | '_ \ / _` | '__/ _ \| |/ _` | \___ \| __| | | |/ _` | |/ _ \
     *   / ___ \| | | | (_| | | | (_) | | (_| |  ___) | |_| |_| | (_| | | (_) |
     *  /_/   \_\_| |_|\__,_|_|  \___/|_|\__,_| |____/ \__|\__,_|\__,_|_|\___/
     * @Description: TODO 封装我们的BaseModel然后,通过观察者模式对数据进行观察并且通知我们的UI进行更新数据
     */
    open class BaseViewModel(application: Application) : AndroidViewModel(application), IBaseViewModel {
        private var mLifecycle: WeakReference? = null
        private var uc: UIChangeLiveData? = null
        private lateinit var context: Application
        /**
         * 所有网络请求都在 viewModelScope 域中启动,当页面销毁时会自动
         * 调用ViewModel的  #onCleared 方法取消所有协程
         */
        fun launchUI(block: suspend CoroutineScope.() -> Unit) {
            //网络可用
            GlobalScope.launch(Dispatchers.IO) {
                viewModelScope.launch { block() }
            }
        }
        override fun onAny(owner: LifecycleOwner?, event: Lifecycle.Event?) {
        }
        override fun onCreate() {
        }
        override fun onDestroy() {
        }
        override fun onStart() {
        }
        override fun onStop() {
        }
        override fun onResume() {
        }
        override fun onPause() {
        }
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        }
        /**
         * 过滤请求结果,其他全抛异常
         * @param block 请求体
         * @param success 成功回调
         * @param error 失败回调
         * @param complete  完成回调(无论成功失败都会调用)
         * @param isShowDialog 是否显示加载框
         */
        fun  launchOnlyresult(
            block: suspend CoroutineScope.() -> IBaseResponse,
            success: (T?) -> Unit,
            error: (ResponseThrowable) -> Unit = {
    //            LogUtils.e("错误异常"+it.localizedMessage)
                it.printStackTrace()
            },
            complete: () -> Unit = {},
            isShowDialog: Boolean = true
        ) {
            if (isShowDialog) uc?.getShowDialog()?.call()
            launchUI {
                handleException(
                    {
                        withContext(Dispatchers.IO) {
                            block().let {
                                if (it.isSuccess()) {
                                    it.data()
                                } else {
                                    uc?.toastEvent()?.postValue(it.errorMsg())
                                    throw ResponseThrowable(it.errorCode(), it.errorMsg())
                                }
                            }
                        }.also {
                            success(it)
                        }
                    },
                    {
                        error(it)
                    },
                    {
                        uc?.getDismissDialog()?.call()
                        complete()
                    }
                )
            }
        }
        /**
         * 异常统一处理
         */
        private suspend fun handleException(
            block: suspend CoroutineScope.() -> Unit,
            error: suspend CoroutineScope.(ResponseThrowable) -> Unit,
            complete: suspend CoroutineScope.() -> Unit
        ) {
            coroutineScope {
                try {
                    block()
                } catch (e: Throwable) {
                    error(ExceptionHandle.handleException(e))
                } finally {
                    complete()
                }
            }
        }
        /**
         * 注入RxLifecycle生命周期
         *
         * @param lifecycle
         */
        fun injectLifecycleProvider(lifecycle: LifecycleProvider?) {
            mLifecycle = WeakReference(lifecycle)
        }
        /**
         * 跳转页面
         *
         * @param clz 所跳转的目的Activity类
         */
        fun startActivity(clz: Class?) {
            startActivity(clz, null)
        }
        /**
         * @param clz  clz 所跳转的目的Activity类
         * @param code 启动requestCode
         */
        fun startActivity(clz: Class?, code: Int) {
            startActivity(clz, null, code)
        }
        /**
         * 跳转页面
         *
         * @param clz    所跳转的目的Activity类
         * @param bundle 跳转所携带的信息
         */
        private fun startActivity(clz: Class?, bundle: Bundle?) {
            startActivity(clz, bundle, REQEUST_DEFAULT)
        }
        /**
         * 跳转页面
         *
         * @param clz    所跳转的目的Activity类
         * @param bundle 跳转所携带的信息
         */
        fun startActivity(clz: Class?, bundle: Bundle?, requestCode: Int) {
            val params: MutableMap = HashMap()
            params[CLASS] = clz
            params[REQUEST] = requestCode
            params[BUNDLE] = bundle
            uc?.getStartActivityEvent()?.postValue(params as Map)
        }
        fun startActivityForFragment(clz: Class, bundle: Bundle, requestCode: Int) {
            val params: MutableMap = HashMap()
            params[CLASS] = clz
            params[REQUEST] = requestCode
            params[BUNDLE] = bundle
            uc?.getStartActivityForFragment()?.postValue(params)
        }
        /**
         * 关闭界面
         */
        fun finish() {
            uc?.getFinishEvent()?.postValue(null)
        }
        /**
         * 携带code的 finish
         */
        fun finishFragmentResult() {
            uc?.getResultFragment()?.postValue(null)
        }
        /**
         * 返回上一层
         */
        fun onBackPressed() {
            uc?.getOnBackPressedEvent()!!.postValue(null)
        }
        fun getUC(): UIChangeLiveData? {
            if (uc == null) {
                uc = UIChangeLiveData()
            }
            return uc
        }
        companion object {
            class UIChangeLiveData : SingleLiveEvent() {
                private var startActivityEvent: SingleLiveEvent? = null
                private var finishEvent: SingleLiveEvent? = null
                private var onBackPressedEvent: SingleLiveEvent? = null
                private var setResultEvent: SingleLiveEvent? = null
                private var finishResult: SingleLiveEvent? = null
                private var startActivityForFragment: SingleLiveEvent? = null
                private var setResultFragment: SingleLiveEvent? = null
                private var showDialog: SingleLiveEvent? = null
                private var toastEvent: SingleLiveEvent? = null
                private var dismissDialog: SingleLiveEvent? = null
                fun getShowDialog(): SingleLiveEvent {
                    return createLiveData(showDialog).also {
                        showDialog = it
                    }
                }
                fun getDismissDialog(): SingleLiveEvent {
                    return createLiveData(dismissDialog).also {
                        dismissDialog = it
                    }
                }
                fun toastEvent(): SingleLiveEvent {
                    return createLiveData(toastEvent).also {
                        toastEvent = it
                    }
                }
                fun getResultFragment(): SingleLiveEvent {
                    return createLiveData(setResultFragment).also {
                        setResultFragment = it
                    }
                }
                fun getStartActivityForFragment(): SingleLiveEvent {
                    return createLiveData(startActivityForFragment).also {
                        startActivityForFragment = it
                    }
                }
                fun getFinishResult(): SingleLiveEvent {
                    return createLiveData(finishResult).also {
                        finishResult = it
                    }
                }
                fun getStartActivityEvent(): SingleLiveEvent {
                    return createLiveData(startActivityEvent).also {
                        startActivityEvent = it
                    }
                }
                fun getSetResultEvent(): SingleLiveEvent {
                    return createLiveData(setResultEvent).also {
                        setResultEvent = it
                    }
                }
                fun getFinishEvent(): SingleLiveEvent {
                    return createLiveData(finishEvent).also {
                        finishEvent = it
                    }
                }
                fun getOnBackPressedEvent(): SingleLiveEvent {
                    return createLiveData(onBackPressedEvent).also {
                        onBackPressedEvent = it
                    }
                }
                private fun  createLiveData(liveData: SingleLiveEvent?): SingleLiveEvent {
                    var mLive: SingleLiveEvent? = liveData
                    liveData?.let {
                        return mLive!!
                    } ?: let {
                        mLive = SingleLiveEvent()
                    }
                    return mLive!!
                }
                override fun observe(owner: LifecycleOwner, observer: Observer) {
                    super.observe(owner, observer)
                }
            }
            object ParameterField {
                const val CLASS = "CLASS"
                const val CANONICAL_NAME = "CANONICAL_NAME"
                const val BUNDLE = "BUNDLE"
                const val REQUEST = "REQUEST"
                const val REQEUST_DEFAULT = 1
            }
        }
    }
    

    目前我们的代码中会几处错误,我一步一步的给大家解决

    首先我们的IBaseViewModel这个接口类没有,我们需要创建一个IBaseViewModel接口类

    package com.example.basemodel.base
    import android.content.Intent
    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.LifecycleObserver
    import androidx.lifecycle.LifecycleOwner
    import androidx.lifecycle.OnLifecycleEvent
    /**
     * @author 浩楠
     *
     * @date 2023/6/4-15:20
     *
     *      _              _           _     _   ____  _             _ _
     *     / \   _ __   __| |_ __ ___ (_) __| | / ___|| |_ _   _  __| (_) ___
     *    / _ \ | '_ \ / _` | '__/ _ \| |/ _` | \___ \| __| | | |/ _` | |/ _ \
     *   / ___ \| | | | (_| | | | (_) | | (_| |  ___) | |_| |_| | (_| | | (_) |
     *  /_/   \_\_| |_|\__,_|_|  \___/|_|\__,_| |____/ \__|\__,_|\__,_|_|\___/
     * @Description: TODO 这里我们使用的是LifecycleObserver,源码中也给出了相应的解释,就是用来对生命周期进行观察的
     */
    interface IBaseViewModel: LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
        fun onAny(owner: LifecycleOwner?, event: Lifecycle.Event?)
        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        fun onCreate()
        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        fun onDestroy()
        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onStart()
        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onStop()
        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        fun onResume()
        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        fun onPause()
        fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
    }
    

    然后是我们的SingleLiveEvent这个类也没有,不要急,我们继续创建,但是我们创建完成后有一个疑问,这个SingleLiveEvent是用来干什么的,它起到了一个什么作用。对于SingleLiveEvent呢,我个人的理解是我们在使用LiveData的时候会存在很多的样板代码,每次添加一个事件就需要添加一个对应的方法,这样不仅容易出错误还可以会导致View忘记调用ViewModel中的方法而SingleLiveEvent刚好可以解决这个问题,但是他也会带来一个新的问题,它仅限于一个观察者。如果你添加了多个,则只会有一个收到回调,并且无法保证哪一个会收到,所以我们对事件进行封装并且来管理,使得减少错误

    package com.example.basemodel.base
    import android.util.Log
    import androidx.annotation.MainThread
    import androidx.lifecycle.LifecycleOwner
    import androidx.lifecycle.MutableLiveData
    import androidx.lifecycle.Observer
    import java.util.concurrent.atomic.AtomicBoolean
    open class SingleLiveEvent : MutableLiveData() {
        private val mPending = AtomicBoolean(false)
        @MainThread
        override fun observe(owner: LifecycleOwner, observer: Observer) {
            if (hasActiveObservers()) {
                Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
            }
            // Observe the internal MutableLiveData
            super.observe(owner) { t ->
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t)
                }
            }
        }
        @MainThread
        override fun setValue(t: T?) {
            mPending.set(true)
            super.setValue(t)
        }
        /**
         * Used for cases where T is Void, to make calls cleaner.
         */
        @MainThread
        fun call() {
            value = null
        }
        companion object {
            private const val TAG = "SingleLiveEvent"
        }
    }
    

    创建完成后,我们需要注释两个地方,一个是launchOnlyresult(我们的网络请求的封装,这个后面我会带大家封装网络请求,暂时先注释掉),还有一个地方是handleException,我们的统一的异常处理这里也需要注释掉。

    我们的BaseViewModel封装好了,那我们该封装BaseActivity和BaseFragment,在封装他们两个之前,我们还需要做一件事情。既然是MVVM项目,双向绑定怎么能少的了我们的dataBinding和viewBinding,所以我们需要在我们的app目录下的build.gradle中添加我们的dataBinding和viewBinding

    Android用kotlin搭建MVVM框架(一),Kotlin在Android中实现MVVM框架入门教程(一) 第2张

      dataBinding {
            enabled = true
        }
        viewBinding{
            enabled = true
        }
    

    添加好以后,我们再创建一个BaseActivity和IBaseView

    package com.example.basemodel.base
    import android.content.Intent
    import android.graphics.Color
    import android.os.Build
    import android.os.Bundle
    import android.view.View
    import android.view.WindowManager
    import android.widget.TextView
    import androidx.databinding.DataBindingUtil
    import androidx.databinding.ViewDataBinding
    import androidx.fragment.app.FragmentActivity
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.ViewModelProvider
    import com.example.basemodel.base.BaseViewModel.Companion.ParameterField.BUNDLE
    import com.example.basemodel.base.BaseViewModel.Companion.ParameterField.CLASS
    import com.example.basemodel.base.BaseViewModel.Companion.ParameterField.REQUEST
    import com.hjq.xtoast.XToast
    import com.kt.ktmvvm.lib.R
    import com.kt.network.dialog.LoadingDialog
    import com.trello.rxlifecycle2.components.support.RxAppCompatActivity
    import java.lang.reflect.ParameterizedType
    /**
     * @author 浩楠
     *
     * @date 2023/6/4-15:58
     *
     *      _              _           _     _   ____  _             _ _
     *     / \   _ __   __| |_ __ ___ (_) __| | / ___|| |_ _   _  __| (_) ___
     *    / _ \ | '_ \ / _` | '__/ _ \| |/ _` | \___ \| __| | | |/ _` | |/ _ \
     *   / ___ \| | | | (_| | | | (_) | | (_| |  ___) | |_| |_| | (_| | | (_) |
     *  /_/   \_\_| |_|\__,_|_|  \___/|_|\__,_| |____/ \__|\__,_|\__,_|_|\___/
     * @Description: TODO 封装一个BaseActivity
     */
    abstract class BaseActivity : RxAppCompatActivity(), IBaseView {
        open var binding: V? = null
        open var viewModel: VM? = null
        open var viewModelId = 0
        private var dialog: LoadingDialog? = null
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            initViewDataBinding(savedInstanceState)
            //页面接受的参数方法
            initParam()
            //私有的ViewModel与View的契约事件回调逻辑
            registerUIChangeLiveDataCallBack()
            //页面事件监听的方法,一般用于ViewModel层转到View层的事件注册
            initViewObservable()
        }
        private fun registerUIChangeLiveDataCallBack() {
            //跳入新页面
            viewModel?.getUC()?.getStartActivityEvent()?.observe(this) { params ->
                params?.let {
                    val clz = params[CLASS] as Class?
                    val intent = Intent(this@BaseActivity, clz)
    //            intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                    val bundle = params[BUNDLE]
                    if (bundle is Bundle) {
                        intent.putExtras((bundle as Bundle?)!!)
                    }
                    startActivityForResult(intent, params[REQUEST] as Int)
                }
            }
            viewModel?.getUC()?.getFinishResult()?.observe(this) { integer ->
                integer?.let {
                    setResult(integer)
                    finish()
                }
            }
            viewModel?.getUC()?.getShowDialog()?.observe(this){
                showLoading()
            }
            viewModel?.getUC()?.getDismissDialog()?.observe(this){
                dismissLoading()
            }
            viewModel?.getUC()?.toastEvent()?.observe(this){
    //            ToastUtils.showShort(it)
                XToast(this).apply {
                    setContentView(R.layout.layout_toast)
                    setDuration(3000)
                    findViewById(R.id.txtToastMessage).text=it
                }.show()
            }
            //关闭界面
            viewModel?.getUC()?.getFinishEvent()?.observe(this) { finish() }
            //关闭上一层
            viewModel?.getUC()?.getOnBackPressedEvent()?.observe(this) { onBackPressed() }
            viewModel?.getUC()?.getSetResultEvent()?.observe(this) { params ->
                params?.let {
                    val intent = Intent()
                    if (params.isNotEmpty()) {
                        val strings: Set = params.keys
                        for (string in strings) {
                            intent.putExtra(string, params[string])
                        }
                    }
                    setResult(RESULT_OK, intent)
                }
            }
        }
        override fun onResume() {
            super.onResume()
        }
        private fun initViewDataBinding(savedInstanceState: Bundle?) {
            //DataBindingUtil类需要在project的build中配置 dataBinding {enabled true }, 同步后会自动关联android.databinding包
            binding =
                DataBindingUtil.setContentView(this@BaseActivity, initContentView(savedInstanceState))
            viewModelId = initVariableId()
            val modelClass: Class
            val type = javaClass.genericSuperclass
            modelClass = if (type is ParameterizedType) {
                type.actualTypeArguments[1] as Class
            } else {
                //如果没有指定泛型参数,则默认使用BaseViewModel
                BaseViewModel::class.java
            }
            viewModel = createViewModel(this, modelClass as Class)
            //关联ViewModel
            binding?.setVariable(viewModelId, viewModel)
            //支持LiveData绑定xml,数据改变,UI自动会更新
            binding?.lifecycleOwner = this
            //让ViewModel拥有View的生命周期感应
            lifecycle.addObserver(viewModel!!)
            //注入RxLifecycle生命周期
            viewModel?.injectLifecycleProvider(this)
        }
        /**
         * 打开等待框
         */
        private fun showLoading() {
            if (dialog==null){
                dialog= LoadingDialog.getInstance(this)
            }
            dialog?.show()
        }
        /**
         * 打开等待框
         */
        private fun dismissLoading() {
            dialog?.run {
                if (isShowing) dismiss()
            }
        }
        override fun onDestroy() {
            super.onDestroy()
            binding?.unbind()
        }
        /**
         * 创建ViewModel 如果 需要自己定义ViewModel 直接复写此方法
         *
         * @param cls
         * @param 
         * @return
         */
        open fun  createViewModel(activity: FragmentActivity?, cls: Class?): T {
            return ViewModelProvider(activity!!)[cls!!]
        }
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)
            viewModel?.onActivityResult(requestCode, resultCode, data)
        }
        /**
         * 提供livedata 或者flow 数据流观察回调
         */
        override fun initViewObservable() {
        }
        /**
         * 返回vaeriableId
         */
        abstract fun initVariableId(): Int
        /**
         * 返回布局id
         */
        abstract fun initContentView(savedInstanceState: Bundle?): Int
        /**
         * Toast
         */
        fun showMsg(msg:String){
            XToast(this).apply {
                setContentView(R.layout.layout_toast)
                setDuration(3000)
    //            showAsDropDown(view)
                findViewById(R.id.txtToastMessage).text=msg
            }.show()
        }
    }
    
    package com.example.basemodel.base
    /**
     * @author 浩楠
     *
     * @date 2023/6/4-16:10
     *
     *      _              _           _     _   ____  _             _ _
     *     / \   _ __   __| |_ __ ___ (_) __| | / ___|| |_ _   _  __| (_) ___
     *    / _ \ | '_ \ / _` | '__/ _ \| |/ _` | \___ \| __| | | |/ _` | |/ _ \
     *   / ___ \| | | | (_| | | | (_) | | (_| |  ___) | |_| |_| | (_| | | (_) |
     *  /_/   \_\_| |_|\__,_|_|  \___/|_|\__,_| |____/ \__|\__,_|\__,_|_|\___/
     * @Description: TODO 通过接口类来实现我们需要的方法,并且便于后期我们对base的新增和修改
     */
    interface IBaseView {
        /**
         * 初始化界面传递参数
         */
        fun initParam()
        /**
         * 初始化界面观察者的监听
         */
        fun initViewObservable()
    }
    

    封装好我们的BaseActivity后BaseFragment也就出来了,因为他们的生命周期很相似不说,他们的封装也没有太大的区别,所以我们就直接上代码

    package com.example.basemodel.base
    import android.content.Intent
    import android.os.Bundle
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.TextView
    import androidx.databinding.DataBindingUtil
    import androidx.databinding.ViewDataBinding
    import androidx.fragment.app.Fragment
    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.ViewModelProvider
    import com.hjq.xtoast.XToast
    import com.kt.ktmvvm.lib.R
    import com.kt.network.dialog.LoadingDialog
    import com.trello.rxlifecycle2.components.support.RxAppCompatActivity
    import com.trello.rxlifecycle2.components.support.RxFragment
    import java.lang.reflect.ParameterizedType
    /**
     * @author 浩楠
     *
     * @date 2023/6/4-17:23
     *
     *      _              _           _     _   ____  _             _ _
     *     / \   _ __   __| |_ __ ___ (_) __| | / ___|| |_ _   _  __| (_) ___
     *    / _ \ | '_ \ / _` | '__/ _ \| |/ _` | \___ \| __| | | |/ _` | |/ _ \
     *   / ___ \| | | | (_| | | | (_) | | (_| |  ___) | |_| |_| | (_| | | (_) |
     *  /_/   \_\_| |_|\__,_|_|  \___/|_|\__,_| |____/ \__|\__,_|\__,_|_|\___/
     * @Description: TODO 封装一个BaseFragment
     */
    abstract class BaseFragment : RxFragment(), IBaseView {
        open var binding: V? = null
        open var viewModel: VM? = null
        open var viewModelId = 0
        var dialog: LoadingDialog? = null
        //是否第一次加载
        private var isFirst: Boolean = true
        @Deprecated("Deprecated in Java")
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            initParam()
        }
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = DataBindingUtil.inflate(
                inflater,
                initContentView(inflater, container, savedInstanceState),
                container,
                false
            ) as V?
            return binding?.root
        }
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            onVisible()
            viewModel?.let { lifecycle.addObserver(it) }
            //私有的初始化Databinding和ViewModel方法
            initViewDataBinding()
            //私有的ViewModel与View的契约事件回调逻辑
            registerUIChangeLiveDataCallBack()
            //页面事件监听的方法,一般用于ViewModel层转到View层的事件注册
            initViewObservable()
        }
        override fun onResume() {
            super.onResume()
            onVisible()
        }
        /**
         * 是否需要懒加载
         */
        private fun onVisible() {
            if (lifecycle.currentState == Lifecycle.State.STARTED && isFirst) {
                lazyLoadData()
                isFirst = false
            }
        }
        /**
         * 懒加载
         */
        open fun lazyLoadData() {}
        private fun registerUIChangeLiveDataCallBack() {
            //跳入新页面
            viewModel?.getUC()?.getStartActivityEvent()?.observe(this) { params ->
                params?.let {
                    val clz = params[BaseViewModel.Companion.ParameterField.CLASS] as Class?
                    val intent = Intent(activity, clz)
    //            intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                    val bundle = params[BaseViewModel.Companion.ParameterField.BUNDLE]
                    if (bundle is Bundle) {
                        intent.putExtras((bundle as Bundle?)!!)
                    }
                    this@BaseFragment.startActivityForResult(
                        intent,
                        params[BaseViewModel.Companion.ParameterField.REQUEST] as Int
                    )
                }
            }
            viewModel?.getUC()?.getFinishResult()?.observe(this) { integer ->
                integer?.let {
                    activity?.setResult(integer)
                    activity?.finish()
                }
            }
            viewModel?.getUC()?.getShowDialog()?.observe(this) {
                ShowDialog()
            }
            viewModel?.getUC()?.getDismissDialog()?.observe(this) {
                dismissLoading()
            }
            //关闭界面
    //        viewModel?.getUC()?.getFinishEvent()?.observe(this) { activity?.finish() }
            //关闭上一层
            viewModel?.getUC()?.getOnBackPressedEvent()?.observe(this) { activity?.onBackPressed() }
            viewModel?.getUC()?.getSetResultEvent()?.observe(this) { params ->
                params?.let {
                    val intent = Intent()
                    if (params.isNotEmpty()) {
                        val strings: Set = params.keys
                        for (string in strings) {
                            intent.putExtra(string, params[string])
                        }
                    }
                    activity?.setResult(RxAppCompatActivity.RESULT_OK, intent)
                }
            }
        }
        private fun initViewDataBinding() {
            viewModelId = initVariableId()
            viewModelId = initVariableId()
            val modelClass: Class
            val type = javaClass.genericSuperclass
            modelClass = if (type is ParameterizedType) {
                type.actualTypeArguments[1] as Class
            } else {
                //如果没有指定泛型参数,则默认使用BaseViewModel
                BaseViewModel::class.java
            }
            viewModel = createViewModel(this, modelClass as Class)
            //关联ViewModel
            binding?.setVariable(viewModelId, viewModel)
            //支持LiveData绑定xml,数据改变,UI自动会更新
            binding?.lifecycleOwner = this
            //让ViewModel拥有View的生命周期感应
            lifecycle.addObserver(viewModel!!)
            //注入RxLifecycle生命周期
            viewModel?.injectLifecycleProvider(this)
        }
        open fun  createViewModel(fragment: Fragment?, cls: Class?): T {
            return ViewModelProvider(fragment!!)[cls!!]
        }
        /**
         * 打开等待框
         */
        private fun ShowDialog() {
            if (dialog == null) {
                dialog = LoadingDialog.getInstance(activity)
            }
            dialog?.show()
        }
        /**
         * 打开等待框
         */
        private fun dismissLoading() {
            dialog?.run {
                if (isShowing) dismiss()
            }
        }
        /**
         * 返回variableid
         */
        abstract fun initVariableId(): Int
        /**
         * Toast
         */
        fun showMsg(msg: String) {
            XToast(activity).apply {
                setContentView(R.layout.layout_toast)
                setDuration(3000)
    //            showAsDropDown(view)
                findViewById(R.id.txtToastMessage).text = msg
            }.show()
        }
        /**
         * 返回布局id
         */
        abstract fun initContentView(
            inflater: LayoutInflater?,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): Int
    }
    

    封装好以后呢会有一个问题,我们的Base中有一个showMsg的方法会报错,这个是我个人通过第三方框架进行了一个简单的二次封装进Base中的,具体的页面布局我发出来大家不喜欢可以自行修改

    
        
    
    

    接下来我们来使用一下我们创建封装的Base

    我们创建一个activity然后继承一下我们的BaseActivity

    package com.ghn.cocknovel.ui.activity
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import com.example.basemodel.base.BaseActivity
    import com.example.basemodel.base.BaseViewModel
    import com.ghn.cocknovel.BR
    import com.ghn.cocknovel.R
    import com.ghn.cocknovel.databinding.ActivityHomeBinding
    class HomeActivity : BaseActivity() {
        override fun initVariableId(): Int {
            return BR._all//这里并不是都是_all是根据我们在布局中的layout的data中的variable的name来写的,如果我们的name是aaa这里就是aaa
        }
        override fun initContentView(savedInstanceState: Bundle?): Int {
            return R.layout.activity_home
        }
        override fun initParam() {
            //写页面的一些逻辑,比如调用我们的网络请求
        }
    }
    
    
        
            
        
        
            
        
    
    

    我们可以看到我的布局中会有一个type,对应了一个type这个type中是要给viewmodel,我们也可以自己创建一个viewmodel,然后继承我们的BaseViewModel

    /**
     * @author 浩楠
     *
     * @date 2023/4/6-17:16.
     *
     *      _              _           _     _   ____  _             _ _
     *     / \   _ __   __| |_ __ ___ (_) __| | / ___|| |_ _   _  __| (_) ___
     *    / _ \ | '_ \ / _` | '__/ _ \| |/ _` | \___ \| __| | | |/ _` | |/ _ \
     *   / ___ \| | | | (_| | | | (_) | | (_| |  ___) | |_| |_| | (_| | | (_) |
     *  /_/   \_\_| |_|\__,_|_|  \___/|_|\__,_| |____/ \__|\__,_|\__,_|_|\___/
     * @Description: TODO 这样就可以了,这个ViewModel我们可以用来写网络请求,数据更新
     */
    open class RecommendViewModel(application: Application) : BaseViewModel(application) {
       
    }
    

    本章节到这里也就结束了,在封装的过程中,遇到问题的可以留言,我看到后会第一时间回复

    我就不上效果图了,因为就是一个单纯的Base封装,没什么效果可以看。如有大佬指点一二,小弟在此抱拳了

    Android用kotlin搭建MVVM框架(一),Kotlin在Android中实现MVVM框架入门教程(一) 第3张


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码