Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

mmkv、shared preference和datastore读写工具、委托工具,material3设计的preference组件库(mmkv, shared preference, and datastore read/write tools, delegate tools, and the preference component library designed by material3)

License

Notifications You must be signed in to change notification settings

Knightwood/ComposePreference

Repository files navigation

preference 工具库

包含了sharedPreference、mmkv、datastore的读写工具、委托实现,以及界面组件,以及节点依赖功能。

其中,ui组件 与 AndroidX preference库一样,它可以自动保存并显示首选项

当然,也有跨平台版:链接

英文

版本

** **

dependencies {
 val version = "1.2.7"
 //必选
 implementation("com.github.Knightwood.ComposePreference:preference-data-core:$version")
 //如不需要界面,可以不引入此依赖
 implementation("com.github.Knightwood.ComposePreference:preference-ui-compose:$version")
 //偏好值读写工具,下面三选一,或者自己实现读写接口
 implementation("com.github.Knightwood.ComposePreference:preference-util:$version")
 implementation("com.github.Knightwood.ComposePreference:datastore-util:$version")
 implementation("com.github.Knightwood.ComposePreference:preference-mmkv-util:$version")
}
//注:如果使用mmkv,sharedpreference,你自己的工程不要忘记引入相应mmkv,sharedpreference依赖,以及初始化mmkv等。
  • 一些破坏性改动
    • ui组件重写
    • 方法名 getReadWriteTool 改为 getSingleDataEditor
    • 接口名 IPreferenceReadWrite 改为 IPreferenceEditor

特性:

使用简单,界面和偏好值读写分离

支持切换多种存储/读取方式

可自定义偏好值读写过程工具

提供界面组件启用状态节点依赖功能

可脱离界面,单独使用偏好值读写工具,并提供了统一写入和读取(使用flow观察值的变化)方法

也可以仅使用ui界面

介绍图

支持的存储偏好值的工具

内置三种可用的存储偏好值的工具

  1. DataStore
  2. MMKV
  3. SharedPreference

但是注意,SharedPreference不支持存储double,mmkv不支持set类型,他们所支持的有所差异。 DataStore则不支持同步的方式读取。

还可以继承PreferenceHolderIPreferenceEditor实现额外的存储过程,例如存储到文件、数据库等。

  • 对于仅需要preference读写工具,而不需要ui界面的,可以仅引入com.github.Knightwood.ComposePreference:preference-data-core 和任意一种读写工具。

  • 对于仅需要ui界面,可以仅引入com.github.Knightwood.ComposePreference:preference-ui-composecom.github.Knightwood.ComposePreference:preference-data-core

委托工具类

使用委托工具,可以像读写普通变量一样读写偏好值

mmkv 委托工具

 class MMKVHelper private constructor(val mmkv: MMKV) {
 //使用委托的方式生成一个委托对象,除了[parcelableM]方法,初始值可选
 var name by mv.strM("tom", "初始值")
}
//1. 获取单例
val helper = MMKVHelper.getInstance(prefs)
//2. 使用赋值将值存入
helper.name = "Tom"
//3. 直接使用即读取值,如果没有值写入,读取出来的会是默认值。
log.d(TAG, helper.name)

SharedPreference 委托工具

//注意,PrefsHelper 是单例。
class PrefsHelper private constructor(val prefs: SharedPreferences) {
 var isFinish by prefs.boolean("isFinish")
 var name by prefs.string("name")
 var age by prefs.int("age")
 //***其余无关代码省略
}
//1. 获取单例
val helper = PrefsHelper.getInstance(prefs)
//2. 使用赋值将值存入
helper.name = "Tom"
//3. 直接使用即读取值,如果没有值写入,读取出来的会是默认值。
log.d(TAG, helper.name)

DataStore 委托工具

//1. 需要有一个协程作用域
val scope = CoroutineScope(Dispatchers.IO)
//2. 你可以将属性委托给datastore,变量名就是key的名称
var username by dataStore.getting(11, scope)
//3. 对数据读写就可以存储到datastore
MaterialTheme {
 //可以在compose中观察数据变化
 val va = dataStore.asDataFlow<Int>("username").collectAsState(initial = 11)
 Column {
 Button(onClick = {
 val randoms = Random.nextInt(0, 11)
 //赋值就会将数据写入datastore
 username = randoms
 //访问变量就可以得到刚刚写入的数据
 println(username)
 }) {
 Text("Random")
 }
 Text("value:${va.value}")
 }
}

ui

ui 部分分为了两组api:

  1. cross包下面是纯粹的ui界面,不会自动读取和存储偏好值,也没有节点依赖功能。
  2. auto包下面则是可以自动读取和存储偏好值的界面,并且支持节点依赖功能。

preference组件提供了自定义主题的功能,以修改间距,颜色,字体,边框等。 这对于统一组件的样式比较方便,不用在写界面时每个方法都传入一遍样式相关的参数。

ui的使用

单纯的ui组件

这些组件和方法都在com.kiylx.compose.preference.component.cross包下

使用com.kiylx.compose.preference.theme.Preferences.SetTheme设置和修改主题。

@Composable
fun SettingsScreen() {
 Column(
 modifier = Modifier
 .verticalScroll(rememberScrollState()),
 verticalArrangement = Arrangement.spacedBy(4.dp)
 ) {
 Preferences.SetTheme(
 iconStyle = PreferenceIconStyle(
 paddingValues = PaddingValues(8.dp),
 tint = MaterialTheme.colorScheme.onPrimary,
 backgroundColor = MaterialTheme.colorScheme.primary,
 )
 ) {
 PreferenceItemTest()
 PreferenceSubTitle(
 modifier = Modifier.padding(top = 8.dp),
 title = "其他"
 )
 var progress by remember {
 mutableStateOf(0f)
 }
 PreferenceSlider(
 value = progress,
 desc = "滑动条描述",
 onValueChanged = { progress = it })
 SwitchTest()
 PreferenceSubTitle(title = "多选框", modifier = Modifier)
 CheckBoxTest()
 PreferenceSubTitle(title = "单选框", modifier = Modifier)
 RadioTest()
 PreferenceSubTitle(title = "折叠", modifier = Modifier)
 var expand by remember { mutableStateOf(false) }
 PreferenceCollapseItem(
 expand = expand,
 title = "附加内容",
 stateChanged = { expand = !expand })
 {
 Preferences.SetTheme {
 Column(modifier = Modifier.padding(top = 12.dp, start = 12.dp, end = 12.dp)) {
 PreferenceItemTest()
 }
 }
 }
 }
 }
}

自动存储读取偏好值,带节点依赖的ui组件

  • 要构建一个自动存储偏好值的preference界面,需要使用com.kiylx.compose.preference.component.auto.SetTheme函数 且传入上面支持的三种工具之一(当然你也可以自己继承接口定制额外的存储方式,例如数据库和文件)
@Composable
fun NewComponents2(ctx: Context) {
 //1. 使用dataStore存储偏好值
 val holder = remember {
 DataStorePreferenceHolder.instance(
 dataStoreName = "test",
 ctx = AppCtx.instance
 )
 }
 //2. 使用mmkv存储偏好值
// val holder = remember {
// MMKVPreferenceHolder.instance(MMKV.defaultMMKV())
// }
 //3. 使用sharedprefrence存储偏好值
// val holder = remember {
// OldPreferenceHolder.instance(
// AppCtx.instance.getSharedPreferences(
// "ddd",
// Context.MODE_PRIVATE
// )
// )
// }
 val customNodeName = "customNode"
 //创建一个自定义节点
 val node = holder.registerDependence(customNodeName, true)
 val scope = rememberCoroutineScope()
 Column(
 modifier = Modifier
 .verticalScroll(rememberScrollState()),
 verticalArrangement = Arrangement.spacedBy(4.dp)
 ) {
 Preferences.SetTheme(
 holder = holder,
 iconStyle = PreferenceIconStyle(
 paddingValues = PaddingValues(8.dp),
 tint = MaterialTheme.colorScheme.onPrimary,
 backgroundColor = MaterialTheme.colorScheme.primary,
 )
 ) {
 Column {
 PreferenceSwitch(
 defaultValue = false,
 title = "使用新特性",
 desc = "实验功能,可能不稳定",
 dependenceKey = DependenceNode.rootName,
 keyName = "s1"
 ) { state ->
 //这里获取并修改了当前的enable状态,
 //依赖这个节点的会改变显示状态,
 //如果当前没有指定依赖,自身也会受到影响
 scope.launch {
 holder.getDependence("s1")?.setEnabled(state)
 }
 }
 PreferenceItem(
 dependenceKey = "s1",
 title = "关联组件",
 icon = Icons.Outlined.AccountCircle
 )
 PreferenceSwitchWithContainer(
 title = "调整您的设置信息",
 desc = "账户、翻译、帮助信息等",
 defaultValue = false,
 keyName = "b2",
 dependenceKey = DependenceNode.rootName,
 icon = Icons.Outlined.AccountCircle,
 ) {
 scope.launch {
 node.setEnabled(it)
 }
 }
 PreferenceItem(
 modifier = Modifier,
 title = "账户",
 icon = Icons.Outlined.AccountCircle,
 dependenceKey = customNodeName,
 desc = "本地、谷歌",
 )
 var expand by remember { mutableStateOf(false) }
 PreferenceCollapseItem(
 expand = expand,
 title = "附加内容",
 dependenceKey = customNodeName,
 stateChanged = { expand = !expand })
 {
 Column(modifier = Modifier.padding(horizontal = 16.dp)) {
 PreferenceItem(
 title = "动画",
 icon = Icons.Outlined.TouchApp,
 desc = "动画反馈、触感反馈",
 )
 PreferenceItem(
 title = "语言",
 desc = "中文(zh)",
 icon = Icons.Outlined.Language,
 )
 }
 }
 PreferencesCautionCard(
 title = "调整您的设置信息",
 desc = "账户、翻译、帮助信息等",
 dependenceKey = customNodeName,
 icon = Icons.Outlined.AccountCircle,
 )
 }
 }
 }
}

可用的组件:

大卡片

  • PreferencesCautionCard
  • PreferencesHintCard

preference item

  • PreferenceSubTitle
  • PreferenceItem

switch

  • PreferenceSwitch
  • PreferenceSwitchWithContainer
  • PreferenceSwitchWithDivider

可折叠其他组件的box

  • PreferenceCollapseItem

Radio

  • PreferenceRadio

CheckBox

  • PreferenceCheckBox

Slider

  • PreferenceSlider

依赖和置灰

  • enable使用: preference组件传入enable为false的同时,指定dependenceKey为DependenceNode.rootName,可以置灰组件,使之无法相应事件。

  • 依赖的使用:例如:有三个开关:a,b,c

当开关a切换为off时,将b和c置灰。

原理:我们使用一个mutableState保存enable状态,b和c都观察这个状态,当开关a为off时修改这个状态,b和c就会因为观察这个状态而重组,从而达到目的。

每一个preference可组合函数,都会根据自身的keyName生成一个这样的状态,并将状态保存在上面的holder中,所以要达到开关a为off时禁用b和c,有两种方式:

  1. 自己注册一个状态节点(例如为node1),然后将b,c的dependenceKey指定为node1的name,然后修改这个node1状态
  2. 将b,c的dependenceKey指定为a的keyName,然后获取a的节点状态并进行修改,但是要注意,开关a需要指定dependenceKey为其他,否则a也会受到影响

第一种方式例子:

PreferencesScope(holder = holder) {
 val node = holder.registerDependence("customNode", true)// 1
//PreferenceItem可组合函数
 PreferenceSwitchWithDivider(
 keyName = "bol3",
 title = "title",
 dependenceKey = "customNode", // 2
 description = "description",
 icon = Icons.Filled.CenterFocusWeak
 )
 PreferenceSwitch(
 keyName = "bol2",
 title = "title",
 description = "description",
 icon = Icons.Filled.CenterFocusWeak
 ) {
 node.enableState.value = it //3 修改节点状态
 }
}
  1. 代码1处创建一个了一个自定义状态节点,enable状态为true,并将节点命名为"customNode"
  2. 代码2处表示这个PreferenceItem可组合函数的enable状态依赖于1处创建的名为"customNode"的节点状态
  3. 代码3处根据switch修改了"customNode"的enable状态,此时,依赖此node的可组合函数都会收到影响

第二种方式例子

Preferences.SetTheme(holder = holder) {
 //switch A
 PreferenceSwitch(
 keyName = "bol",
 title = "title",
 dependenceKey = DependenceNode.rootName,//指定依赖为根结点,这样自身就不会受到影响
 description = "description"
 ) { state ->
 //这里获取并修改了当前的enable状态,
 //依赖这个节点的会改变显示状态,
 //如果当前没有指定依赖,自身也会受到影响
 holder.getDependence("bol")?.let {
 it.enableState.value = state
 }
 }
 //switch B
 PreferenceSwitch(
 keyName = "bol2",
 title = "title",
 dependenceKey = "bol", //依赖key为bol的状态
 description = "description",
 icon = Icons.Filled.CenterFocusWeak
 )
}

这个例子中没有new一个node,却能达到效果。

这是因为preference可组合函数会根据自身的keyName和enable参数( switch A 传入的keyName为"bol" ,enable默认为true),生成一个node保存起来。 可以通过调用holder.getDependence(key name)得到状态节点。

switch B 依赖于switch A 注册的enable状态,当A通过getDependence方法获取到节点状态并做出修改时, switch B 就会重组从而置灰。

但我们发现,switch A却没有因为修改状态被置灰,这是因为 switch A 把自己的dependence指定为了一个默认的内置状态节点,所以switch A会受到DependenceNode.rootName节点影响 却不会受到自身节点状态的影响。 若希望switch A收到自身节点状态的影响,只需要switch A不指定dependenceKey,保持它为null即可。

直接使用偏好值读写工具

对于datastore提供了prefStoreHolder.getSingleDataEditor()方式 对于mmkv和SharedPreference,分别提供了两种工具,一种是prefStoreHolder.getSingleDataEditor()方式,一种是委托的方式

prefStoreHolder.getSingleDataEditor()方式

MMKV,SharedPreference,DataStore均支持此种方式,这也是Preference组件所需要的读写工具

//1,获取读写工具
//MMKV
 val prefStoreHolder = MMKVPreferenceHolder.instance(MMKV.defaultMMKV())
//SharedPreference
val prefStoreHolder = OldPreferenceHolder.instance(
 AppCtx.instance.getSharedPreferences("ddd",Context.MODE_PRIVATE)
 )
 
//DataStore
val prefStoreHolder = DataStorePreferenceHolder.instance(
 dataStoreName = "test",
 ctx = AppCtx.instance
 )
//2,获取某个偏好值读写器
val pref =prefStoreHolder.getSingleDataEditor(keyName = keyName, defaultValue = "")
//3,获取偏好值
pref.flow().collect { s ->
	//flow收集到偏好值的变更
}
或者
val currentValue = pref.flow().collectAsState(defaultValue)
//4, 写入偏好值
pref.write("")

About

mmkv、shared preference和datastore读写工具、委托工具,material3设计的preference组件库(mmkv, shared preference, and datastore read/write tools, delegate tools, and the preference component library designed by material3)

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /