原文地址: 关于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. JS+CSS3 360度全景图插件 - Watch3D.js

    日常闲扯 从上一篇文章到这篇中间快过了一年了,时间真滴过得快.不是在下中间没想过写新的文章,而是自己确实变懒了(体重+1 +1 +1 +1....) ..OTL...不过到最后觉得还是需要写点东西,不 ...

  2. python-统计字符个数

    输入一个字符串,统计其中数字字符及小写字符的个数 输入格式: 输入一行字符串 输出格式: 共有?个数字,?个小写字符 输入样例: helo134ss12 输出样例: 共有5个数字,6个小写字符 代码: ...

  3. 嵌入式Servlet容器

    配置嵌入式Servlet容器 ##Spring Boot里面内置了嵌入式的Servlet容器(tomcat) 点击pom.xml->右键->Diagrams->show Depend ...

  4. LC-141andLC-142

    142. 环形链表 II 思路: 设链表共有 a+b 个节点,其中 链表头部到链表入口 有 a 个节点(不计链表入口节点), 链表环 有 b 个节点. 再设两指针分别走了 f,s 步,则有: f = ...

  5. mysql查询 if判断、case语句的使用等

    一. 查询的数字转换为中文返回前端 1. 如果是0或1表状态等,可用: IF(字段 = 0, '否', '是') AS xxx 2. 如果是多个值,比如1,2,3可用: ELT(字段, '计划治理', ...

  6. [已解决] error: cannot convert `int*' to `int**' for argument `2' to `void print_f(int, int**)'

    #include "stdio.h" #include "stdlib.h" #include "time.h" void print_f( ...

  7. Java-NIO之Channel(通道)

    1:Channel是什么 通道表示与实体的开放连接,例如硬件设备.文件.网络套接字或能够执行一个或多个不同 I/O 操作(例如读取或写入)的程序组件. 1.1:Channel与Stream的对比 St ...

  8. docker基础_网络模式

    docker网络 网络模式: bridge:docker默认 自己创建会默认使用bridge模式 类似vmware中的NAT模式 其中192.168.1.203是本机在现实世界局域网的ip.172.1 ...

  9. 关于fiddler抓包一键生成python脚本

    本人贡献一篇关于抓包转换成脚本的文章 步骤一 打开fiddler,抓到包之后,保存成txt文件 步骤二 脚本里str_filename改成保存的文件名 步骤三 执行脚本一键转换 附上脚本,感谢关注~ ...

  10. .NET 程序读取当前目录避坑指南

    前些天有 AgileConfig 的用户反映,如果把 AgileConfig 部署成 Windows 服务程序会启动失败.我看了一下日志,发现根目录被定位到了 C:\Windows\System32 ...