首页 > 其他分享 >android mvvm实例解析

android mvvm实例解析

时间:2023-08-04 23:12:59浏览次数:36  
标签:mvvm viewModel list value fun 实例 var android View

MVVM架构,将整个应用分为三层,View层,VM层,Model层。其中View层单向引用VM层,VM层单向引用Model层。如上图。 单向引用,而非双向引用,这是MVVM与MVP最大的区别。View层,只是单向引用VM层,VM层不需要引用View层,但是却可以 更新View层。这是通过VM层的观察者模式实现的,在这里使用架构组件LiveData,观察者注册LiveData,当LiveData数据发生变更 的时候,就会通知注册的观察者。 VM层,执行业务逻辑,获取Model层的数据,Model层的数据由repository来提供。   举例子: ChooseAreaFragment是View层,它持有ViewModel,它可以监听相关数据,相关数据发生变化的时候,对应的UI就会被更新。 比如:dataChanged数据发生变化,就会执行定义的观察者操作。   viewModel.dataChanged.observe(this, Observer {             adapter.notifyDataSetChanged()             listView.setSelection(0)             closeProgressDialog()         })

class ChooseAreaFragment : Fragment() {


    private val viewModel by lazy { ViewModelProviders.of(this, InjectorUtil.getChooseAreaModelFactory()).get(ChooseAreaViewModel::class.java) }
    private var progressDialog: ProgressDialog? = null
    private lateinit var adapter: ArrayAdapter<String>


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.choose_area, container, false)
        val binding = DataBindingUtil.bind<ChooseAreaBindingImpl>(view)
        binding?.viewModel = viewModel
        return view
    }


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        adapter = ChooseAreaAdapter(context!!, R.layout.simple_item, viewModel.dataList)
        listView.adapter = adapter
        observe()
    }


    private fun observe() {
        viewModel.currentLevel.observe(this, Observer { level ->
            when (level) {
                LEVEL_PROVINCE -> {
                    titleText.text = "中国"
                    backButton.visibility = View.GONE
                }
                LEVEL_CITY -> {
                    titleText.text = viewModel.selectedProvince?.provinceName
                    backButton.visibility = View.VISIBLE
                }
                LEVEL_COUNTY -> {
                    titleText.text = viewModel.selectedCity?.cityName
                    backButton.visibility = View.VISIBLE
                }
            }
        })


        viewModel.dataChanged.observe(this, Observer {
            adapter.notifyDataSetChanged()
            listView.setSelection(0)
            closeProgressDialog()
        })
        viewModel.isLoading.observe(this, Observer { isLoading ->
            if (isLoading) showProgressDialog()
            else closeProgressDialog()
        })
        viewModel.areaSelected.observe(this, Observer { selected ->
            if (selected && viewModel.selectedCounty != null) {
                if (activity is MainActivity) {
                    val intent = Intent(activity, WeatherActivity::class.java)
                    intent.putExtra("weather_id", viewModel.selectedCounty!!.weatherId)
                    startActivity(intent)
                    activity?.finish()
                } else if (activity is WeatherActivity) {
                    val weatherActivity = activity as WeatherActivity
                    weatherActivity.drawerLayout.closeDrawers()
                    weatherActivity.viewModel.weatherId = viewModel.selectedCounty!!.weatherId
                    weatherActivity.viewModel.refreshWeather()
                }
                viewModel.areaSelected.value = false
            }
        })
        if (viewModel.dataList.isEmpty()) {
            viewModel.getProvinces()
        }
    }


    /**
     * 显示进度对话框
     */
    private fun showProgressDialog() {
        if (progressDialog == null) {
            progressDialog = ProgressDialog(activity)
            progressDialog?.setMessage("正在加载...")
            progressDialog?.setCanceledOnTouchOutside(false)
        }
        progressDialog?.show()
    }


    /**
     * 关闭进度对话框
     */
    private fun closeProgressDialog() {
        progressDialog?.dismiss()
    }


    companion object {
        const val LEVEL_PROVINCE = 0
        const val LEVEL_CITY = 1
        const val LEVEL_COUNTY = 2
    }


}
VM层,ViewModel: 使用LiveData包装被View层监听的数据,在VM层数据发生的变化,会通知到View层,但却无需要View层的引用。 因为LiveData应用了观察者模式,注册的观察者,在数据发生变化的时候,会自动通知观察者。 如下,currentLevel,dataChanged,isLoading等都是使用LiveData包装的,意味着,它们发生变化的时候View层会监听得到,从而进行相应的更新操作。 在VM层,持有Model层的引用,Model层的数据获取,网络请求,都依赖repository实现。
class ChooseAreaViewModel(private val repository: PlaceRepository) : ViewModel() {


    var currentLevel = MutableLiveData<Int>()


    var dataChanged = MutableLiveData<Int>()


    var isLoading = MutableLiveData<Boolean>()


    var areaSelected = MutableLiveData<Boolean>()


    var selectedProvince: Province? = null


    var selectedCity: City? = null


    var selectedCounty: County? = null


    lateinit var provinces: MutableList<Province>


    lateinit var cities: MutableList<City>


    lateinit var counties: MutableList<County>


    val dataList = ArrayList<String>()


    fun getProvinces() {
        currentLevel.value = LEVEL_PROVINCE
        launch {
            provinces = repository.getProvinceList()
            dataList.addAll(provinces.map { it.provinceName })
        }
    }


    private fun getCities() = selectedProvince?.let {
        currentLevel.value = LEVEL_CITY
        launch {
            cities = repository.getCityList(it.provinceCode)
            dataList.addAll(cities.map { it.cityName })
        }
    }


    private fun getCounties() = selectedCity?.let {
        currentLevel.value = LEVEL_COUNTY
        launch {
            counties = repository.getCountyList(it.provinceId, it.cityCode)
            dataList.addAll(counties.map { it.countyName })
        }
    }


    fun onListViewItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
        when {
            currentLevel.value == LEVEL_PROVINCE -> {
                selectedProvince = provinces[position]
                getCities()
            }
            currentLevel.value == LEVEL_CITY -> {
                selectedCity = cities[position]
                getCounties()
            }
            currentLevel.value == LEVEL_COUNTY -> {
                selectedCounty = counties[position]
                areaSelected.value = true
            }
        }
    }


    fun onBack() {
        if (currentLevel.value == LEVEL_COUNTY) {
            getCities()
        } else if (currentLevel.value == LEVEL_CITY) {
            getProvinces()
        }
    }


    private fun launch(block: suspend () -> Unit) = viewModelScope.launch {
        try {
            isLoading.value = true
            dataList.clear()
            block()
            dataChanged.value = dataChanged.value?.plus(1)
            isLoading.value = false
        } catch (t: Throwable) {
            t.printStackTrace()
            Toast.makeText(CoolWeatherApplication.context, t.message, Toast.LENGTH_SHORT).show()
            dataChanged.value = dataChanged.value?.plus(1)
            isLoading.value = false
        }
    }


}
Model层: 在这个例子中,Model层对外提供的方法是 getProvinceList,getCityList,getCountyList。 它的数据来源,可能是数据库Dao,或者是网络,各自的实现,再委托到具体的方法去实现。
class PlaceRepository private constructor(private val placeDao: PlaceDao, private val network: CoolWeatherNetwork) {


    suspend fun getProvinceList() = withContext(Dispatchers.IO) {
        var list = placeDao.getProvinceList()
        if (list.isEmpty()) {
            list = network.fetchProvinceList()
            placeDao.saveProvinceList(list)
        }
        list
    }


    suspend fun getCityList(provinceId: Int) = withContext(Dispatchers.IO) {
        var list = placeDao.getCityList(provinceId)
        if (list.isEmpty()) {
            list = network.fetchCityList(provinceId)
            list.forEach { it.provinceId = provinceId }
            placeDao.saveCityList(list)
        }
        list
    }


    suspend fun getCountyList(provinceId: Int, cityId: Int) = withContext(Dispatchers.IO) {
        var list = placeDao.getCountyList(cityId)
        if (list.isEmpty()) {
            list = network.fetchCountyList(provinceId, cityId)
            list.forEach { it.cityId = cityId }
            placeDao.saveCountyList(list)
        }
        list
    }


    companion object {


        private var instance: PlaceRepository? = null


        fun getInstance(placeDao: PlaceDao, network: CoolWeatherNetwork): PlaceRepository {
            if (instance == null) {
                synchronized(PlaceRepository::class.java) {
                    if (instance == null) {
                        instance = PlaceRepository(placeDao, network)
                    }
                }
            }
            return instance!!
        }


    }


}
以上就是MVVM的实例解析。应用MVVM的时候,关键是划分功能属于哪一个层次,然后,再确定引用关系。划分功能属于哪个层次,可以依据单一职责原则,让功能代码原子化,再在这一基础上去区分层次。    

标签:mvvm,viewModel,list,value,fun,实例,var,android,View
From: https://www.cnblogs.com/ttylinux/p/17607261.html

相关文章

  • Android面试必看手册,错过了金三银四可别再错过金九银十了
    眼看着时间一天一天地过去,距离金九银十也就二十多天的日子了,还有多少程序员是两眼摸黑不知道面试要做哪些准备的朋友?还不知道进大厂需要复习哪些资料的朋友可以看过来,知道有些朋友会没什么准备跟无头苍蝇一样所以博主早在一个月前就已经在各大网站和教育平台收集了大量面试相关的资......
  • 通过一个实例了解 va_list
    VA_LIS是在C语言中解决【变参问题】的一组宏,【变参问题】是指参数的个数不定,可以传入一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活。其中va_list(VA_LIST是在C语言中解决变参问题的一......
  • 金九银十就要来了,为Android面试者准备的最全面试题+答案解析
    马上要到金九银十了,小编总结了一些面试题目包含百度/腾讯/小米/网易/搜狗/知乎/京东/360/瓜子,现在放上来,由于是自己整理,所以涵盖不全面的话诸位请谅解。抽象类与接口的区别?大体区别如下:抽象类可以提供成员方法的实现细节,而接口中只能存在public抽象方法;抽象类中的成员变量可以是......
  • android 休眠唤醒机制分析— wake_lock
    Android的休眠唤醒主要基于wake_lock机制,只要系统中存在任一有效的wake_lock,系统就不能进入深度休眠,但可以进行设备的浅度休眠操作。wake_lock一般在关闭lcd、tp但系统仍然需要正常运行的情况下使用,比如听歌、传输很大的文件等。Wakelock-wakelock在android的电源管理系统中扮演......
  • 自己开发的Android 软件发布贴(11月6日)
    Android开发其实去年就开始了,只是当时还只会用ActionScript因为原生Android开发需要学习java语言。从使用下来看,ActionScript运行效能不高做出来的软件基本没法用。经过几个月学习java现在已经能掌握Android开发给大家带来一些自己写的软件,希望能给大家带来帮助所有软件没有......
  • 使用Vue+Vite搭建在线 C++ 源代码混淆工具,带在线实例
    就酱紫github开源地址:https://github.com/dffxd-suntra/cppdgithub在线实例:https://dffxd-suntra.github.io/cppd/预览图片:长截屏背景图重复了,抱歉......
  • AndroidStudio2021.3logcat工具无法显示日志解决办法
    AndroidStudio2021.3logcat工具无法显示日志解决办法 https://blog.csdn.net/weixin_43623271/article/details/127876964  1.File->setting2.搜索logcat,->ExperimentalunChkEnablenewLogcattoolwindow以后提示时Dismissit ......
  • mybatis mapper接口实例化原理
       面试题来了:“我们都知道mybatis的mapper接口是没有实现类的,在使用的时候你知道它是如何实例化的吗?”懵逼的我:“知道啊,用的是jdk自带的动态代理;”;饥渴的面试官:“嗯,没错,继续说,它底层做了哪些事情?”;懵逼的我:“就是动态代理啊,还有啥?”得意的面试官:“这样子啊,那你回去等......
  • Android开发 Jetpack Compose Button
    前言  此篇博客讲解Button按钮一个简单的例子快速了解一下效果图代码@ComposablefunAPage(){Column(Modifier.fillMaxSize(),horizontalAlignment=Alignment.CenterHorizontally,verticalArrangement=Arrangement.Center){......
  • DB2删除实例
    要删除实例:以具有root用户权限的用户身份登录。可选:如果您确定不再需要关联数据库中的数据,您可以在删除实例之前从系统中删除数据库文件或删除数据库。通过输入以下命令删除实例:DB2DIR/instance/db2idropInstName其中 DB2DIR是您在Db2数据库产品安装期间指定的位......