├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── markdown-navigator-enh.xml ├── markdown-navigator.xml ├── misc.xml └── vcs.xml ├── ReadMe.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── vove7 │ │ └── smartkey │ │ └── demo │ │ ├── AppConfig.kt │ │ └── MainActivity.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot └── Screenshot.jpg ├── settings.gradle ├── smartkey-android ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── cn │ │ └── vove7 │ │ └── smartkey │ │ └── android │ │ ├── AndroidSettings.kt │ │ ├── AndroidSettingsImpl.kt │ │ ├── BaseConfigExt.kt │ │ └── SettingsExt.kt │ └── test │ └── java │ ├── ClearTest.kt │ ├── ListTest.kt │ ├── MapTest.kt │ ├── NoCacheConfig.kt │ ├── RunConfig.kt │ ├── SetTest.kt │ ├── SmartKeySyncTest.kt │ ├── Test.kt │ ├── TestNoCacheKey.kt │ └── TestSyncFileSettings.kt └── smartkey ├── .gitignore ├── build.gradle ├── libs └── multiplatform-settings-common-0.1.1.jar └── src └── main └── java ├── cn └── vove7 │ └── smartkey │ ├── BaseConfig.kt │ ├── annotation │ └── Config.kt │ ├── collections │ ├── ObserveableList.kt │ ├── ObserveableMap.kt │ └── ObserveableSet.kt │ ├── key │ ├── IKey.kt │ ├── KeyConfig.kt │ ├── NoCacheKey.kt │ └── SmartKey.kt │ ├── settings │ ├── BaseSyncFileSetting.kt │ ├── FileSettings.kt │ ├── JsonSettings.kt │ └── PropertiesSettings.kt │ └── tool │ ├── JsonHelper.kt │ ├── Vog.kt │ └── encryptor │ ├── AESEncryptor.kt │ ├── Base64.java │ └── Encryptor.kt └── com └── russhwolf └── settings ├── Delegates.kt ├── ExperimentalListener.kt ├── Operators.kt └── Settings.kt /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | xmlns:android 17 | 18 | ^$ 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 | xmlns:.* 28 | 29 | ^$ 30 | 31 | 32 | BY_NAME 33 | 34 |
35 |
36 | 37 | 38 | 39 | .*:id 40 | 41 | http://schemas.android.com/apk/res/android 42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | .*:name 51 | 52 | http://schemas.android.com/apk/res/android 53 | 54 | 55 | 56 |
57 |
58 | 59 | 60 | 61 | name 62 | 63 | ^$ 64 | 65 | 66 | 67 |
68 |
69 | 70 | 71 | 72 | style 73 | 74 | ^$ 75 | 76 | 77 | 78 |
79 |
80 | 81 | 82 | 83 | .* 84 | 85 | ^$ 86 | 87 | 88 | BY_NAME 89 | 90 |
91 |
92 | 93 | 94 | 95 | .* 96 | 97 | http://schemas.android.com/apk/res/android 98 | 99 | 100 | ANDROID_ATTRIBUTE_ORDER 101 | 102 |
103 |
104 | 105 | 106 | 107 | .* 108 | 109 | .* 110 | 111 | 112 | BY_NAME 113 | 114 |
115 |
116 |
117 |
118 | 119 | 121 |
122 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | -------------------------------------------------------------------------------- /.idea/markdown-navigator-enh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | [![](https://jitpack.io/v/Vove7/SmartKey.svg)](https://jitpack.io/#Vove7/SmartKey) 2 | 3 | # SmartKey 4 | 5 | > 利用Kotlin委托实现优雅地持久化存储应用配置。 6 | 7 | 1. 支持纯Kotlin项目 8 | 2. 支持Android项目 9 | 3. 支持自定义持久化实现 10 | 4. 空安全 11 | 5. 配置动态监听 12 | 13 | 14 | - [基本使用](#基本使用) 15 | - [更多操作](#更多操作) 16 | - [基本存储实现](#基本存储实现) 17 | - [自定义持久化实现](#自定义持久化实现) 18 | - [引入SmartKey](#引入SmartKey) 19 | - [注意事项](#注意事项) 20 | 21 | ### 基本使用 22 | 23 | #### 初始化 24 | 25 | 这里可以使用注解 `@Config("app_config", implCls = ...)` 配置存储文件名,多个配置类可分文件存储。 26 | **安卓项目可以无需指明implCls,自动使用 `SharedPreference` 做存储;使用数据库需要适配,参考: [自定义持久化实现](#自定义持久化实现)** 27 | 28 | 1. 定义配置类 29 | ```kotlin 30 | //注解非必须,不加注解,会使用默认配置路径,和默认存储实现 31 | @Config("app_config", implCls = JsonSettings::class) 32 | object AppConfig { 33 | 34 | //基本类型存储 35 | var text: String by smartKey("a") 36 | 37 | //可空基础类型 38 | var nullableInt: Int? by smartKey(null) 39 | var number: Int by smartKey(50) 40 | 41 | //数组 42 | var intArr: Array by smartKey(emptyArray()) 43 | 44 | //实体类 45 | var userInfo: UserInfo? by smartKey(null, encrypt = true) 46 | 47 | //实体数组 [操作即更新] 默认空列表 48 | var modelList by smartKeyList() 49 | //实体集 [操作即更新] 默认空集 50 | var modelSet by smartKeySet(emptySet()) 51 | // [操作即更新] 52 | var map by smartKeyMap() 53 | 54 | } 55 | 56 | //数组实体 57 | data class ListModel(val s: String, val a: Int) 58 | 59 | //实体类 60 | data class UserInfo( 61 | var name: String, 62 | var email: String, 63 | var age: Int 64 | ) 65 | 66 | ``` 67 | 68 | 69 | 2. 此时你可以像这样使用 70 | 71 | ```kotlin 72 | //获取存储值 73 | val value = AppConfig.text 74 | val n = AppConfig.number 75 | 76 | //实时存储 77 | AppConfig.text = "setValue" 78 | AppConfig.number = 0 79 | 80 | //存储登录用户数据 81 | val user = UserInfo("new_user", "xx@xx.xx", 0) 82 | AppConfig.userInfo = user 83 | 84 | //注意修改实体中的属性无法触发持久化操作 85 | user.name = "hello" 86 | //需要赋值操作触发 87 | AppConfig.userInfo = user 88 | 89 | //操作 实时存储 无需显式赋值;需要注意避免循环 add, 可以使用addAll 90 | AppConfig.modelList.add(ListModel("string", 1)) 91 | //set map 操作同 List 实时存储 92 | 93 | ``` 94 | 95 | 3. 配置类附加功能 96 | 97 | 继承`BaseConfig`拥有配置类基础操作 98 | ```kotlin 99 | object AppConfig : BaseConfig { 100 | //... 101 | } 102 | ``` 103 | ```kotlin 104 | 105 | // 清空此配置所有key 106 | AppConfig.clear() 107 | 108 | // 直接存储key 109 | AppConfig["key"] = 1 //key, value 110 | AppConfig["text"] = "abc" //key, value 111 | 112 | // 加密储存(目前只支持String及实体类型加密) 113 | AppConfig["key", true] = "value" 114 | "key" in AppConfig // is contains key 115 | AppConfig -= "key" // remove key 116 | 117 | // 获取可空类型的值 118 | val strNullable: String? = AppConfig["dont_exists_key"] 119 | 120 | //提供默认值 以获取不可空值 121 | val s :String = AppConfig["text", "default"] //key, default 122 | 123 | 124 | // 获取解密后的数据 125 | val value :String? = AppConfig["key", true] 126 | 127 | 128 | //获取可空数据 129 | val user :UserInfo? = AppConfig["userInfo"] 130 | // or 131 | val user = AppConfig.get("userInfo") 132 | 133 | //获取加密内容 134 | val user: UserInfo? = AppConfig["userInfo", true] 135 | 136 | ``` 137 | 138 | ### demo 139 | 140 | 见app目录 141 | 142 | 143 | 144 | ### 更多操作 145 | 146 | - 你可以指定变量对应存储的key: 147 | ```kotlin 148 | object AppConfig2 : BaseConfig { //指定key 149 | //import cn.vove7.smartkey.smartKey 150 | var text: String by smartKey("defaultValue", key = "your_key") 151 | 152 | var textWithKey: String by smartKey("aaa", key="text_key") 153 | 154 | //安卓项目可通过resId指定keyId 155 | //import cn.vove7.smartkey.android.smartKey 156 | var textAndroid: String by smartKey("defaultValue", keyId = R.string.key) 157 | } 158 | ``` 159 | 160 | - key 动态绑定 161 | 162 | ```kotlin 163 | print(AppConfig2.textWithKey) //aaa 164 | AppConfig2["text_key"] = "bbb" 165 | print(AppConfig2.textWithKey) //bbb 166 | ``` 167 | 168 | - 选择是否加密数据: 169 | 170 | ```kotlin 171 | //使用encrypt来声明加密存储数据 172 | var userInfo: UserInfo? by smartKey(null, encrypt = true) 173 | 174 | ``` 175 | 176 | - 为每个配置类设置存储实现 177 | 178 | > 不指定`implCls`时, 默认实现为`JsonSettings` 179 | 180 | ```kotlin 181 | @Config(implCls = FileSettings::class) 182 | class AppConfig1 { 183 | 184 | } 185 | 186 | @Config(implCls = PropertiesSettings::class) 187 | class AppConfig2 { 188 | 189 | } 190 | ``` 191 | 192 | 193 | - 无缓存的NoCacheKey 194 | 195 | 由于`SmartKey`会对value进行缓存,在多进程会存在问题。因此而生的`NoCacheKey`,保证读取的数据是实时的。 196 | 使用和SmartKey基本一致。 197 | 198 | 另外,在使用基于文件存储的Settings时,修改文件配置后,`NoCacheKey`可以监听文件变化,来载入最新配置。 199 | 200 | ```kotlin 201 | var text: String by noCacheKey("defaultValue", key = "your_key") 202 | ``` 203 | 204 | ### 基本存储实现 205 | 206 | - JsonSettings 207 | 208 | 使用json格式存储配置。 209 | 210 | - PropertiesSettings 211 | 212 | 基于java `PropertiesSettings`持久化 213 | 可设置`baseDir` `PropertiesSettings.baseDir = "..."` 214 | 215 | - FileSettings 216 | 217 | 使用文件存储。 218 | 可设置`baseDir` `FileSettings.baseDir = "..."` 219 | 220 | **其中 `JsonSettings` 和 `PropertiesSettings` 继承与 `BaseSyncFileSetting`,配置可随文件修改重新加载到内存。为达到此目的,你需要使用 `NoCacheKey` 来确保实时读取到的是修改后的配置** 221 | 222 | ### 自定义持久化实现 223 | 224 | 1. 实现`com.russhwolf.settings.Settings`接口 225 | 226 | ```kotlin 227 | //必须存在构造函数(val configName:String) 228 | 229 | class MySettingsImpl(val configName:String) : Settings 230 | ``` 231 | 232 | 2. 使用自定义实现类 233 | 234 | 在配置类设置注解参数`implCls`: 235 | 236 | ```kotlin 237 | @Config(implCls = MySettingsImpl::class) 238 | class AppConfig { 239 | 240 | } 241 | ``` 242 | 243 | ### 引入SmartKey 244 | 245 | ###### Step 1. Add it in your root build.gradle at the end of repositories: 246 | ```groovy 247 | allprojects { 248 | repositories { 249 | //... 250 | maven { url 'https://jitpack.io' } 251 | } 252 | } 253 | ``` 254 | ###### Step 2. Add the dependency 255 | 256 | - Kotlin 257 | 258 | ```groovy 259 | dependencies { 260 | implementation "com.github.Vove7.SmartKey:smartkey:$lastest_version" 261 | } 262 | ``` 263 | 264 | - Android 265 | 266 | ```groovy 267 | dependencies { 268 | implementation "com.github.Vove7.SmartKey:smartkey-android:$lastest_version" 269 | } 270 | ``` 271 | > lastest_version : [![](https://jitpack.io/v/Vove7/SmartKey.svg)](https://jitpack.io/#Vove7/SmartKey) 272 | 273 | ### 注意事项 274 | 275 | 在开启混淆时,请注意添加规则以保证SmartKey正常工作。 276 | 277 | 1. 由于实体类的持久化使用了Gson,请保证实体类字段名不被混淆,或使用注解@SerializedName 278 | 2. 在没有指定key的config实例,例如 val a :Int by smaryKey(0),由于没有指定key,会默认使用变量名作为key,请保证变量名不被混淆(本人未验证kotlin编译混淆后,property name是否被改变) 279 | 280 | 281 | ### TODO 282 | 283 | - DynamicKey 284 | 285 | this key can reset to 0 everyday: 286 | 287 | ```kotlin 288 | val dk by synamicKey(0) { 289 | SimpleDateFormat("yyyy-MM-dd").format(Date()) + "_today_xxx_count" 290 | } 291 | ``` 292 | 293 | - ExpirableKey 294 | 295 | 296 | ### Thanks 297 | 298 | - [Kotlin](https://kotlinlang.org/) 299 | - 底层存储使用[multiplatform-settings](https://github.com/russhwolf/multiplatform-settings) 300 | - [Gson](https://github.com/googlo/gson) 301 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 28 9 | defaultConfig { 10 | applicationId "cn.vove7.smartkey" 11 | minSdkVersion 21 12 | targetSdkVersion 28 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 28 | implementation 'androidx.appcompat:appcompat:1.1.0' 29 | testImplementation 'junit:junit:4.12' 30 | 31 | // implementation 'com.github.Vove7:SmartKey:2.0.0' 32 | implementation project(':smartkey-android') 33 | } 34 | 35 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/cn/vove7/smartkey/demo/AppConfig.kt: -------------------------------------------------------------------------------- 1 | package cn.vove7.smartkey.demo 2 | 3 | import cn.vove7.smartkey.AConfig 4 | import cn.vove7.smartkey.R 5 | import cn.vove7.smartkey.android.AndroidSettings 6 | import cn.vove7.smartkey.android.smartKey 7 | import cn.vove7.smartkey.annotation.Config 8 | import cn.vove7.smartkey.key.smartKeyList 9 | 10 | /** 11 | * # AppConfig 12 | * 13 | * @author 11324 14 | * 2019/4/21 15 | */ 16 | @Config(implCls = AndroidSettings::class) 17 | object AppConfig : AConfig() { 18 | 19 | //基本类型存储 20 | var text: String by smartKey("a", keyId = R.string.key_text, encrypt = true) 21 | 22 | //可空基础类型 23 | var nullableInt: Int? by smartKey(null) 24 | var number: Int by smartKey(50) 25 | 26 | //数组 27 | var intArr: Array by smartKey(emptyArray()) 28 | 29 | //实体类 30 | var userInfo: UserInfo? by smartKey(null, encrypt = true) 31 | 32 | //实体数组 33 | var modelList: MutableList by smartKeyList() 34 | 35 | var boolVar by smartKey(true) 36 | 37 | fun commit() { 38 | (settings as AndroidSettings).syncImmediately() 39 | } 40 | } 41 | 42 | data class UserInfo( 43 | val name: String 44 | ) 45 | 46 | class ListModel(val s: String, val a: Int) 47 | -------------------------------------------------------------------------------- /app/src/main/java/cn/vove7/smartkey/demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package cn.vove7.smartkey.demo 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.text.Editable 6 | import android.text.TextWatcher 7 | import android.view.View 8 | import androidx.appcompat.app.AppCompatActivity 9 | import cn.vove7.smartkey.R 10 | import cn.vove7.smartkey.get 11 | import cn.vove7.smartkey.tool.PrintListener 12 | import cn.vove7.smartkey.tool.Vog 13 | import kotlinx.android.synthetic.main.activity_main.* 14 | import java.util.* 15 | 16 | 17 | class MainActivity : AppCompatActivity() { 18 | 19 | val lis: PrintListener = { 20 | log_text.append(it + "\n") 21 | scrollView.postDelayed({ 22 | scrollView.smoothScrollTo(0, log_text.height + 100) 23 | }, 10) 24 | } 25 | 26 | override fun onDestroy() { 27 | super.onDestroy() 28 | Vog.removeListener(lis) 29 | } 30 | 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | 33 | Vog.init(0)//输出日志 34 | Vog.addListener(lis) 35 | 36 | super.onCreate(savedInstanceState) 37 | setContentView(R.layout.activity_main) 38 | 39 | Vog.d("nullableInt: " + AppConfig.nullableInt) 40 | AppConfig.nullableInt = 9 41 | Vog.d("nullableInt: " + AppConfig.nullableInt) 42 | /** 43 | * 测试 set get 44 | */ 45 | val user = AppConfig.get("userInfo") 46 | 47 | val user2: UserInfo? = AppConfig["userInfo"] 48 | val user3: UserInfo? = AppConfig["userInfo", true] 49 | 50 | Vog.i("boolVar: ${AppConfig.boolVar}") 51 | 52 | // AppConfig["text"] = "234" //key, value 53 | 54 | //number_picker 55 | number_picker.apply { 56 | minValue = 0 57 | maxValue = 100 58 | setOnValueChangedListener { _, oldVal, newVal -> 59 | AppConfig.number = newVal 60 | } 61 | value = AppConfig.number 62 | } 63 | 64 | //输入框 65 | edit_text.setText(AppConfig.text) 66 | edit_text.addTextChangedListener(object : TextWatcher { 67 | override fun afterTextChanged(s: Editable?) { 68 | s ?: return 69 | AppConfig.text = s.toString() 70 | AppConfig.userInfo = UserInfo(s.toString()) 71 | } 72 | 73 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} 74 | 75 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} 76 | }) 77 | 78 | //数组 TextView 79 | arr_text.text = Arrays.toString(AppConfig.intArr) 80 | //add按钮 81 | add_arr.setOnClickListener { 82 | AppConfig.intArr = arrayOf(*AppConfig.intArr, Random().nextInt(100)) 83 | arr_text.text = Arrays.toString(AppConfig.intArr) 84 | } 85 | //remove按钮 86 | remove_arr.setOnClickListener { 87 | try { 88 | val l = mutableListOf(*AppConfig.intArr) 89 | l.removeAt(0) 90 | AppConfig.intArr = l.toTypedArray() 91 | } catch (e: Exception) { 92 | Vog.d("已无元素") 93 | } 94 | arr_text.text = AppConfig.intArr.contentToString() 95 | } 96 | switch_bool.setOnClickListener { 97 | AppConfig.boolVar = !AppConfig.boolVar 98 | Vog.i("boolVar: ${AppConfig.boolVar}") 99 | } 100 | } 101 | 102 | fun clearAndReload(view: View) { 103 | AppConfig.clear() 104 | finish() 105 | startActivity(Intent(this, MainActivity::class.java)) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 19 | 20 | 23 | 24 |