原文地址: 关于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(
val key: String,
val defaultValue: T,
var currentValue: T = defaultValue,
val initLbd: (GlobalDataConfig) -> 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的有所区别

关于TornadoFx和Android的全局配置工具类封装实现及思路解析的更多相关文章

  1. Android实用的Toast工具类封装

    Toast这个提示框大家都晓得,显示一段时间后自动消失,不能获得焦点.但是在使用中有些问题: 1)需要弹出一个新的Toast时,上一个Toast还没有显示完2)可能重复弹出相同的信息3)Toast具体 ...

  2. wemall app商城源码android开发MD5加密工具类

    wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享android开发MD5加密工具类主要代码,供 ...

  3. Android开发调试日志工具类[支持保存到SD卡]

    直接上代码: package com.example.callstatus; import java.io.File; import java.io.FileWriter; import java.i ...

  4. Android开源项目大全 - 工具类

    主要包括那些不错的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多媒体相关及其他. 一.依赖注入DI 通过依赖注入减少Vie ...

  5. Android弹出Toast工具类总结

    Android弹出Toast工具类总结,包括系统自带的,也包括自定义的. public class ToastUtil { public ToastUtil() { } public static T ...

  6. Android 中替代 sharedpreferences 工具类的实现

    Android 中替代 sharedpreferences 工具类的实现 背景 想必大家一定用过 sharedpreferences 吧!就我个人而言,特别讨厌每次 put 完数据还要 commit. ...

  7. 获取 Android APP 版本信息工具类(转载)

    获取 Android APP 版本信息工具类 获取手机APP版本信息工具类 1.获取版本名称 2.获取版本号 3.获取App的名称 package com.mingyue.nanshuibeidiao ...

  8. Android 获取手机的厂商、型号、Android系统版本号等工具类(转载)

    Android 获取手机的厂商.型号.Android系统版本号等工具类 1.获取手机制造厂商 2.获取手机型号 3.获取手机系统当前使用的语言 4.获取Android系统版本号 5.获取手机IMEI串 ...

  9. (转载) 百度地图工具类封装(包括定位,附近、城市、范围poi检索,反地理编码)

    目录视图 摘要视图 订阅 赠书 | 异步2周年,技术图书免费选      程序员8月书讯      项目管理+代码托管+文档协作,开发更流畅 百度地图工具类封装(包括定位,附近.城市.范围poi检索, ...

随机推荐

  1. VasSonic Android源码解析

    VasSonic是腾讯推出的为了提高H5页面首屏加载速度而推出的高性能Hybrid框架,目前广泛应用在QQ商城等Hybrid界面中,以提高用户体验. https://github.com/Tencen ...

  2. springAop必导jar包

    SpringAop:的底层就是通过JDK动态代理"或"CGLib动态代理为技术目标织入横切逻辑. 做aop:需要导入: spring-aop-4.1.5.RELEASE.jar s ...

  3. MyBatis起步搭建

    1 步骤 数据库环境 创建Maven项目 导入依赖 编写MyBatis配置文件 编写MyBatis工具类 编写实体类 编写Mapper 测试 2 数据库环境 MySQL 8.0版本 create da ...

  4. spatialite-tools 安装

    spatialite-tools 安装 官网 https://www.gaia-gis.it/fossil/spatialite-tools/index 下载地址 https://www.gaia-g ...

  5. MVC:开发模式&&三层架构:软件设计架构

    MVC:开发模式 jsp演变历史 早期只有servlet,只能使用response输出标签数据,非常麻烦 后来又jsp,简化了Servlet的开发,如果过度使用jsp,在jsp中即写大量的java代码 ...

  6. JavaWeb学习day5-Servlet初学

  7. R语言_格兰因果检验

    #当前文件路径 getwd() #设置当前路径,注意转译 setwd("C://Users//Administrator//Desktop//R_test") #导入数据 data ...

  8. 有意思的CVE-2022-0337复现

    前言 前两天在刷tw,看到了个比较有意思的一个CVE漏洞,价值奖励是10000美刀,比较好奇的是价值10000美刀的漏洞是什么样子的,漏洞利用就是需要在浏览器中进行用户交互才能触发该漏洞,但由于 Wi ...

  9. XCTF练习题---CRYPTO---混合编码解析

    XCTF练习题---CRYPTO---混合编码解析 flag:cyberpeace{welcometoattackanddefenceworld} 解题步骤: 1.观察题目,下载附件进行查看 2.看到 ...

  10. python自动化测试工具selenium使用指南

    概述 selenium是网页应用中最流行的自动化测试工具,可以用来做自动化测试或者浏览器爬虫等.官网地址为:https://www.selenium.dev/.相对于另外一款web自动化测试工具QTP ...