• 关于TornadoFx和Android的全局配置工具类封装实现及思路解析


    本文为作者原创,转载请注明出处,谢谢配合
    作者:stars-one
    链接:https://www.cnblogs.com/stars-one/p/16320957.html

    本篇大约有6657个字,阅读预计需要8.32分钟


    原文地址: 关于TornadoFx和Android的全局配置工具类封装实现及思路解析 - Stars-One的杂货小窝

    目前个人开发软件存在设置页面,可以让用户自定义些设置,但我发现,存储数据的代码逻辑实在是有些繁琐(保存及APP打开的设置初始化)

    于是便是花了些精力研究了些,封装了个简单的工具类,可以快捷实现存储数据的保存及初始化

    目标

    首先,我们知道,设置的选项值需要存放在本地,之后重新进入APP的时候,需要先从本地读取,若是本地读取不到,才赋予一个默认值

    所以,确认下我们要达到的理想目标:

    对于设置的某项数据,可以使用一个字段进行对应,而不用关心存储保存本地的更新操作和APP初始化读取数值的

    实现步骤

    先提及下思路,我们将数值保存的本地方法,其实无非就是使用File对象创建个文件,之后将数据写入文件接口实现配置

    在TornadoFx中,提供了config对象供我们快速使用,而无需编写过多的关于文件流的操作的代码

    PS:TornadoFx中,除了config,还有个Preference对象,但Preference是写入注册表的,所以这里我们不采用这种方式,详情可以看上一篇TornadoFx设置保存功能(config和preference使用) - Stars-One的杂货小窝

    而在Android中,也是存在有个SharePreference的对象,可以存储写简单的数据

    TornadoFx和Android的方法大同小异,我们以Android的方法为例讲解,后面会附有相关的源码,复制即可使用

    1.实现本地存储数值

    这里,由于是Android,使用了SharePreference对象来存储,由于SharePreference的使用需要Context参数,为了方便封装,用了个开源库,封装好了可以直接使用

    以一个开关设置项为例(boolean数值),写个简单的类:

    class GlobalDataConfig(val key:String) {
        var flag = false
    
        fun setValue(newVal: Boolean) {
            flag = newVal
            updateLocalStorage(newVal)
        }
    
        /**
         *更新本地存储
         *
         * @param newVal
         */
        private fun updateLocalStorage(newVal: Boolean) {
            SPUtils.getInstance().put(key, newVal)
        }
    }
    

    上面这样写,调用的时候,我们需要新建个类,然后设置去的初始值,之后更新统一走setValue()方法,里面已经包含了数据存储在本地的逻辑

    PS: SPUtilsAndroidUtilCode库的工具类,用于快速设置SharePreference

    如果按照上面的来的话,每个设置项都得新建个类,使用极其不优雅,我们接下来进行优化

    2.任意数值(泛型)

    首先,我们需要可以自定义任意类型的(虽然说是任意类型,其实最终还是得看SharePreference支持存储上面数据),一般我们用基本数据类型存储即可(存储对象的话就会十分麻烦)

    那这个时候有个问题摆在眼前,我们如何获取用户传递的数值类型?

    这个时候,泛型就派上用场了

    我们可以这样写:

    class GlobalDataConfig<T>(val key:String,var currentValue:T) {
    
        fun setValue(newVal: T) {
            currentValue = newVal
            updateLocalStorage(currentValue)
        }
    
        /**
         *更新本地存储
         *
         * @param newVal
         */
        private fun updateLocalStorage(value: T) {
            //各种类型的存储
            if (value is Boolean) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Float) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is String) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Int) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Long) {
                SPUtils.getInstance().put(key, value)
            }
        }
    }
    

    这样,我们就可以通过构造函数来生成不同对象.来代表不同的数值项了

    3.初始值

    到了这步,我们还可以想到,进入APP的时候,配置项要进行初始化,这个时候应该是先从本地存储读取,若是读取不同,则是设置默认值

    最初的想法是,使用个函数,用作初始化的数值读取,同时加个变量用来存储默认值(之后可以重置为默认值)

    class GlobalDataConfig<T>(
        val key:String,
        var currentValue:T,
        var defaultValue:T,
        val lbd:((GlobalDataConfig<T>)->Unit)
    ) {
    
        init{
            lbd.invoke(this)
        }
    
        fun setValue(newVal: T) {
            currentValue = newVal
            updateLocalStorage(currentValue)
        }
    
        /**
         *更新本地存储
         *
         * @param newVal
         */
        private fun updateLocalStorage(value: T) {
            if (value is Boolean) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Float) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is String) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Int) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Long) {
                SPUtils.getInstance().put(key, value)
            }
        }
    }
    

    使用:

    GlobalDataConfig("mykey",false,false){
        it.currentValue = SPUtils.getInstance().getBoolean(key, it.defalutValue)
    }
    

    这样使用一看,发现,我们连最初的currentValue都不用设置了

    所以构造参数还能再精简下,让currentValue默认等于defaultValue(这样设置起始没有毛病,因为之后每次都是会走初始化的步骤,从本地存储中读取数据的)

    class GlobalDataConfig<T>(
        val key: String,
        val defaultValue: T,
        var currentValue: T = defaultValue,
        val initLbd: (GlobalDataConfig<T>) -> Unit
    ) {
        init{
            lbd.invoke(this)
        }
        
        fun setValue(newVal: T) {
            currentValue = newVal
            updateLocalStorage(currentValue)
        }
        
        /**
         *更新本地存储
         *
         * @param newVal
         */
        private fun updateLocalStorage(value: T) {
            if (value is Boolean) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Float) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is String) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Int) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Long) {
                SPUtils.getInstance().put(key, value)
            }
        }
    }

    然后用起来就变成了这样:

    GlobalDataConfig("mykey",false){
        it.currentValue = SPUtils.getInstance().getBoolean(key, it.defalutValue)
    }
    

    但是,看起来还是有些繁琐,中间初始化的过程能否再优化呢?

    刚开始我是没有思路的,因为currentValue在类里面是T类型,而我们通过getBoolean等方法,获得的都是Boolean,String等类型,与T类型不对应,IDE里会提示我们语法不对

    然后,突然灵光一闪,我们可以强转类型嘛,如将GlobalDataConfig<T>转为GlobalDataConfig<Boolean>

    代码最终即可以改为下面的样子

    class GlobalDataConfig<T>(
        val key: String,
        val defaultValue: T,
        var currentValue: T = defaultValue
    ) {
        init {
            when{
                defaultValue is Boolean -> {
                    val item = this as GlobalDataConfig<Boolean>
                    item.setValue(SPUtils.getInstance().getBoolean(key,defaultValue))
                }
                defaultValue is String -> {
                    val item = this as GlobalDataConfig<String>
                    item.setValue(SPUtils.getInstance().getString(key,defaultValue))
                }
                defaultValue is Int -> {
                    val item = this as GlobalDataConfig<Int>
                    item.setValue(SPUtils.getInstance().getInt(key,defaultValue))
                }
                defaultValue is Double -> {
                   //SPUtils里面的似乎没有提供获取Double方法...
                }
                else -> kotlin.error("不支持的数据类型!!目前只支持string,boolean,intdouble四种类型")
            }
        }
    
        /**
         * 重置当前值为默认值
         */
        fun resetValue() {
            setValue(defaultValue)
        }
    
        /**
         * 更改数值
         */
        fun setValue(value: T) {
            //更新内存的
            currentValue = value
    
            //更新本地存储的数据
            updateLocalStorage(value)
        }
    
        /**
         * 更新本地存储
         */
        private fun updateLocalStorage(value: T) {
            if (value is Boolean) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Float) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is String) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Int) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Long) {
                SPUtils.getInstance().put(key, value)
            }
        }
    }
    

    使用上也很方便:

    val openAutoRead =GlobalDataConfig("mykey",true)
    

    使用

    稍微补充下使用说明吧

    1.新建全局配置类

    这里为了方便管理,是建了个Constants常量池

    class GlobalData {
        companion object {
            //是否为VIP(默认不是)
            val userStatus = GlobalDataConfig(Constants.SP_USER_STATUS, false)
        }
    }
    

    2.读取数值

    在你需要用的地方,获取数值

    val result = GlobalData.userStatus.currentValue
    

    2.更新数值

    GlobalData.userStatus.setValue(true)
    

    3.重置数值

    GlobalData.userStatus.resetValue()
    

    源码-Android工具类

    PS:这里其实还可以做个扩展,比如说加个回调方法列表,每次setValue方法后,执行所有回调方法,实现类似监听数值变动

    限于实际情况,我就没有扩展了(各位可以参考下TornadoFx中的GlobalDataConfig的实现)

    class GlobalDataConfig<T>(
        val key: String,
        val defaultValue: T,
        var currentValue: T = defaultValue
    ) {
        init {
            when{
                defaultValue is Boolean -> {
                    val item = this as GlobalDataConfig<Boolean>
                    item.setValue(SPUtils.getInstance().getBoolean(key,defaultValue))
                }
                defaultValue is String -> {
                    val item = this as GlobalDataConfig<String>
                    item.setValue(SPUtils.getInstance().getString(key,defaultValue))
                }
                defaultValue is Int -> {
                    val item = this as GlobalDataConfig<Int>
                    item.setValue(SPUtils.getInstance().getInt(key,defaultValue))
                }
                defaultValue is Double -> {
                   //SPUtils里面的似乎没有提供获取Double方法...
                }
                else -> kotlin.error("不支持的数据类型!!目前只支持string,boolean,intdouble四种类型")
            }
        }
    
        /**
         * 重置当前值为默认值
         */
        fun resetValue() {
            setValue(defaultValue)
        }
    
        /**
         * 更改数值
         */
        fun setValue(value: T) {
            //更新内存的
            currentValue = value
    
            //更新本地存储的数据
            updateLocalStorage(value)
        }
    
        /**
         * 更新本地存储
         */
        private fun updateLocalStorage(value: T) {
            if (value is Boolean) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Float) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is String) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Int) {
                SPUtils.getInstance().put(key, value)
            }
            if (value is Long) {
                SPUtils.getInstance().put(key, value)
            }
        }
    }
    

    源码-TornadoFx工具类

    TornadoFx这边源码稍微有点多,就不放出来了,详情可以去我的Github库common-controls查阅,里面也含有详细的使用说明(文档的第7节)

    TornadoFx这边有些特殊,是结合了JavaFx中提供的可观察对象一起连用,使用上与Android的有所区别

  • 相关阅读:
    二维码智慧门牌管理系统升级解决方案
    Allegro 17.4 SMD 敷铜,Shape 敷铜属性更改, Gerber 生成
    mysql中遇到查询字段的别名与函数冲突问题
    Docker
    Bert不完全手册5. 推理提速?训练提速!内存压缩!Albert
    外汇天眼:外汇投资小白必读! 4大外汇交易常见提问释疑
    户外耳机品牌哪个好、最新的户外耳机品牌排行
    数据结构总结
    矩阵分析与应用(21)
    设备远程运维的策略与实践
  • 原文地址:https://www.cnblogs.com/stars-one/p/16320957.html