【问题标题】:Implement a split preference in Android在 Android 中实现拆分首选项
【发布时间】:2022-10-31 16:30:57
【问题描述】:

我觉得必须有一种直接的方法来实现这一点,但到目前为止我还没有找到它。

从本质上讲,在股票设置应用程序中,有一些偏好是“拆分”的——即,点击偏好的文本会做一件事,而点击右侧的图标会做另一件事。

我尝试制作一个扩展PreferenceGroup 的自定义首选项,但似乎PreferenceGroup 并没有像LinearLayout 那样真正包装子视图,而是将所有子视图放在它下面。我尝试从LinearLayout 扩展,但似乎PreferenceScreen 只允许Preferences 作为孩子。

我的问题或多或少是最好的方法是什么:

  1. 创建一个自定义首选项,以某种方式将其他首选项作为子项并正确显示它们(这似乎很理想,但我不确定如何实现它)
  2. 创建一个自定义首选项,当被点击时,它会以某种方式确定哪个部分被点击并运行正确的处理程序(这似乎更简单,特别是如果很多它被硬编码到 Kotlin 类中以获得首选项,而不是被在 XML 中配置)
  3. 其他一些我没有想到的东西。

    就像我说的,这似乎不应该太难,但我还没有在 SO 上找到任何其他关于它的最新问题。我确实找到了this question,但它在 2012 年被询问和回答,没有非常明确的说明。还有this question 询问一些类似但已近11 岁的问题,唯一的答案是相当无用。

    如果有什么我完全忽略了(也许我一直在使用错误的搜索词??)或者您有什么建议,请告诉我!谢谢!

【问题讨论】:

    标签: android user-interface android-preferences preferences preferenceactivity


    【解决方案1】:

    因此,设置页面的 xml 格式似乎不太可能,因此我决定在 Jetpack Compose 中实现我的设置活动。我的解决方案绝不是最优雅的(并且需要更多的 cmets,我知道),但是如何以“正确”的方式做到这一点绝对超出了我的知识范围。

    可组合首选项.kt:

    // TODO your package here
    
    import android.content.Context
    import androidx.compose.foundation.layout.Column
    import androidx.compose.foundation.layout.Row
    import androidx.compose.foundation.selection.toggleable
    import androidx.compose.material.ContentAlpha
    import androidx.compose.material3.*
    import androidx.compose.runtime.*
    import androidx.compose.ui.Modifier
    import androidx.preference.PreferenceManager
    
    abstract class ComposablePreference<T>(val key: String) {
        abstract var value: T
        val onPreferenceChangeListener: MutableList<ComposablePreferenceChangeListener<T>> = ArrayList()
    
        fun shouldPersistChange(newValue: T): Boolean {
            for (preferenceChangeListener in onPreferenceChangeListener) {
                if (!preferenceChangeListener.onPreferenceChange(this, newValue)) return false
            }
            return true
        }
    
        abstract fun getValue(default: T): T
    }
    
    class EphemeralPreference<T>(key: String, override var value: T) : ComposablePreference<T>(key) {
        override fun getValue(default: T): T {
            return value
        }
    }
    
    class BooleanPreference(key: String, val context: Context) : ComposablePreference<Boolean>(key) {
        override var value
            get() = getValue(false)
            set(value) {
                if (!shouldPersistChange(value)) return
                PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
                    putBoolean(key, value)
                    apply()
                }
            }
    
        override fun getValue(default: Boolean): Boolean {
            return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(key, default)
        }
    }
    
    class StringPreference(key: String, val context: Context) : ComposablePreference<String>(key) {
        override var value: String
            get() = getValue("")
            set(value) {
                if (!shouldPersistChange(value)) return
                PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
                    putString(key, value)
                    apply()
                }
            }
    
        override fun getValue(default: String): String {
            return PreferenceManager.getDefaultSharedPreferences(context).getString(key, default)
                   ?: default
        }
    }
    
    class StringSetPreference(key: String, val context: Context) :
        ComposablePreference<Set<String>>(key) {
        override var value: Set<String>
            get() = getValue(HashSet())
            set(value) {
                if (!shouldPersistChange(value)) return
                PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
                    putStringSet(key, value)
                    apply()
                }
            }
    
        override fun getValue(default: Set<String>): Set<String> {
            return PreferenceManager.getDefaultSharedPreferences(context).getStringSet(key, default)
                   ?: default
        }
    }
    
    class FloatPreference(key: String, val context: Context) : ComposablePreference<Float>(key) {
        override var value: Float
            get() = getValue(0f)
            set(value) {
                if (!shouldPersistChange(value)) return
                PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
                    putFloat(key, value)
                    apply()
                }
            }
    
        override fun getValue(default: Float): Float {
            return PreferenceManager.getDefaultSharedPreferences(context).getFloat(key, default)
        }
    }
    
    interface ComposablePreferenceChangeListener<T> {
        fun onPreferenceChange(preference: ComposablePreference<T>, newValue: T): Boolean
    }
    
    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun StringPreferenceChange(
        preference: ComposablePreference<String>,
        dismissDialog: () -> Unit,
        context: Context,
        title: String,
        validate: ((String) -> String)? = null
    ) {
        var newValue by remember { mutableStateOf(preference.value) }
        var message by remember { mutableStateOf( "" )}
        AlertDialog(onDismissRequest = { dismissDialog() }, dismissButton = {
            OutlinedButton(onClick = dismissDialog) {
                Text(text = context.getString(android.R.string.cancel))
            }
        }, confirmButton = {
            Button(onClick = {
                message = validate?.invoke(newValue) ?: ""
                if (message.isBlank()) {
                    return@Button
                }
                preference.value = newValue
                dismissDialog()
            }) {
                Text(text = context.getString(android.R.string.ok))
            }
    
        }, title = {
            Text(text = title)
        }, text = {
            Column {
                OutlinedTextField(value = newValue, onValueChange = { newValue = it })
                Text(
                        text = message,
                        style = MaterialTheme.typography.labelSmall,
                        color = MaterialTheme.colorScheme.onSurface.copy(
                                alpha = ContentAlpha.medium)
                )
            }
    
        })
    }
    
    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun FloatPreferenceChange(
        preference: ComposablePreference<Float>,
        dismissDialog: () -> Unit,
        context: Context,
        title: String,
        validate: ((Float) -> String)? = null
    ) {
        var newValue by remember { mutableStateOf(preference.value.toString()) }
        var message by remember {
            mutableStateOf("")
        }
        AlertDialog(onDismissRequest = { dismissDialog() }, dismissButton = {
            OutlinedButton(onClick = dismissDialog) {
                Text(text = context.getString(android.R.string.cancel))
            }
        }, confirmButton = {
            Button(onClick = {
                try {
                    val temp = newValue.toFloat()
                    message = validate?.invoke(temp) ?: ""
                    if (message.isNotBlank()) {
                        return@Button
                    }
                    preference.value = newValue.toFloat()
                    dismissDialog()
                } catch (e: NumberFormatException) {
                    message = context.getString(R.string.invalid_float)
                }
            }) {
                Text(text = context.getString(android.R.string.ok))
            }
    
        }, title = {
            Text(text = title)
        }, text = {
            Column {
                OutlinedTextField(value = newValue, onValueChange = { newValue = it }, isError =
                message.isNotBlank())
                    Text(
                            text = message,
                            style = MaterialTheme.typography.labelSmall,
                            color = MaterialTheme.colorScheme.onSurface.copy(
                                    alpha = ContentAlpha.medium)
                    )
                }
            })
    }
    
    @Composable
    fun MultiSelectListPreferenceChange(
        preference: ComposablePreference<Set<String>>,
        dismissDialog: () -> Unit,
        context: Context,
        title: String,
        entriesRes: Int,
        entryValuesRes: Int
    ) {
        val newValue = remember {
            mutableStateMapOf(*preference.value.map {
                Pair(
                    it, true
                )
            }.toTypedArray())
        }
        val entries = context.resources.getStringArray(entriesRes)
        val entryValues = context.resources.getStringArray(entryValuesRes)
    
        AlertDialog(onDismissRequest = { dismissDialog() }, dismissButton = {
            OutlinedButton(onClick = dismissDialog) {
                Text(text = context.getString(android.R.string.cancel))
            }
        }, confirmButton = {
            Button(onClick = {
                preference.value = newValue.keys
                dismissDialog()
            }) {
                Text(text = context.getString(android.R.string.ok))
            }
    
        }, title = {
            Text(text = title)
        }, text = {
            Column {
                for ((index, entry) in entries.withIndex()) {
                    Row(
                        modifier = Modifier.toggleable(value = newValue.containsKey(entry),
                                                       onValueChange = {
                                                           if (it) {
                                                               newValue[entry] = true
                                                           } else {
                                                               newValue.remove(entry)
                                                           }
                                                       })
                    ) {
                        Checkbox(
                            checked = newValue.containsKey(entry), onCheckedChange = null/*{
                                    if (it) {
                                        newValue[entry] = true
                                    } else {
                                        newValue.remove(entry)
                                    }
                                }*/
                        )
                        Text(text = entryValues[index])
                    }
                }
            }
    
        })
    }
    
    fun multiselectListPreferenceSummary(
        value: Set<String>, entries: Array<String>, values: Array<String>
    ): String {
        val summaryList: MutableList<String> = ArrayList(value.size)
        for (i in values.indices) {
            if (value.contains(values[i])) summaryList.add(entries[i])
        }
        return summaryList
            .joinToString(", ")
    }
    
    @Composable
    fun CheckboxAction(value: Boolean) {
        Switch(checked = value, onCheckedChange = {})
    }
    

    设置活动.kt:

    @OptIn(ExperimentalMaterial3Api::class)
        @Composable
        fun PreferenceScreen(
                preferences: List<Pair<Pair<Int, (@Composable () -> Unit)?>, @Composable () -> Unit>>,
                selected: Int,
                currentPreferenceGroup: @Composable () -> Unit,
                screenClass: WindowWidthSizeClass,
                onGroupSelected: (key: Int, preferences: @Composable () -> Unit) -> Unit,
                snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }
        ) {
    
            // Remember a SystemUiController
            val systemUiController = rememberSystemUiController()
            val useDarkIcons = !isSystemInDarkTheme()
            val scrim = MaterialTheme.colorScheme.scrim
    
            if (selected == 0) {
                onGroupSelected(preferences.firstOrNull()?.first?.first ?: 0, preferences.firstOrNull()
                    ?.second ?: {})
            }
    
            val phone = screenClass == WindowWidthSizeClass.Compact
    
            DisposableEffect(systemUiController, useDarkIcons) {
                // Update all of the system bar colors to be transparent, and use
                // dark icons if we're in light theme
                systemUiController.setNavigationBarColor(
                        color = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Color.Transparent
                        else scrim,
                        darkIcons = useDarkIcons
                )
    
                // setStatusBarColor() and setNavigationBarColor() also exist
    
                onDispose {}
            }
    
            val phoneScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
            val tabletScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
            Scaffold(topBar = {
                if (phone) {
                    LargeTopAppBar(colors = TopAppBarDefaults.largeTopAppBarColors(
                        containerColor = MaterialTheme.colorScheme.primary,
                        scrolledContainerColor = MaterialTheme.colorScheme.primary,
                        titleContentColor = MaterialTheme.colorScheme.onPrimary,
                        navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
                    ), title = {
                        Text(
                            getString(R.string.title_activity_settings),
                            maxLines = 1,
                            overflow = TextOverflow.Ellipsis
                        )
                    }, scrollBehavior = phoneScrollBehavior, navigationIcon = {
                        IconButton(onClick = {
                            onBackPressedDispatcher.onBackPressed()
                        }) {
                            Icon(
                                Icons.Default.ArrowBack, getString(
                                    R.string.back
                                )
                            )
                        }
                    })
                } else {
                    TopAppBar(colors = TopAppBarDefaults.smallTopAppBarColors(
                        containerColor = MaterialTheme.colorScheme.primary,
                        scrolledContainerColor = MaterialTheme.colorScheme.primary,
                        titleContentColor = MaterialTheme.colorScheme.onPrimary,
                        navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
                    ), title = {
                        Text(
                            getString(R.string.title_activity_settings),
                            maxLines = 1,
                            overflow = TextOverflow.Ellipsis
                        )
                    }, scrollBehavior = tabletScrollBehavior, navigationIcon = {
                        IconButton(onClick = {
                            onBackPressedDispatcher.onBackPressed()
                        }) {
                            Icon(
                                Icons.Default.ArrowBack, getString(
                                    R.string.back
                                )
                            )
                        }
                    })
                }
            },
                     snackbarHost = { SnackbarHost(snackbarHostState) },
                     modifier = Modifier.nestedScroll(if (phone) phoneScrollBehavior
                             .nestedScrollConnection else tabletScrollBehavior.nestedScrollConnection),
                     containerColor = MaterialTheme.colorScheme.background
            ) {
                if (screenClass == WindowWidthSizeClass.Compact) {
                    LazyColumn(
                        modifier = Modifier
                            .selectableGroup()
                            .waterfallPadding(), contentPadding = it
                    ) {
                        items(
                            items = preferences,
                            key = { preference -> preference.first.first }) { preference ->
                            PreferenceGroup(
                                title = preference.first.first,
                                icon = preference.first.second,
                                onClick = {
                                    onGroupSelected(preference.first.first, preference.second)
                                },
                                selected = preference.first.first == selected,
                                tablet = screenClass != WindowWidthSizeClass.Compact,
                                preferences = preference.second,
                                first = preference.first.first == preferences.firstOrNull()?.first?.first
                            )
                        }
                    }
                } else {
                    Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.Start,
                        verticalAlignment = Alignment.Top) {
                        LazyColumn(
                            modifier = Modifier
                                .selectableGroup()
                                .waterfallPadding()
                                .fillMaxWidth(0.35f)
                                .fillMaxHeight()
                                .background(MaterialTheme.colorScheme.surfaceVariant),
                            contentPadding = it,
                            horizontalAlignment = Alignment.CenterHorizontally
                        ) {
                            items(
                                items = preferences,
                                key = { preference -> preference.first.first }) { preference ->
                                PreferenceGroup(
                                    title = preference.first.first,
                                    icon = preference.first.second,
                                    onClick = {
                                        onGroupSelected(preference.first.first, preference.second)
                                    },
                                    selected = preference.first.first == selected,
                                    tablet = screenClass != WindowWidthSizeClass.Compact,
                                    preferences = preference.second,
                                    first = preference.first.first == preferences.firstOrNull()?.first?.first
                                )
                            }
                        }
                        Column(modifier = Modifier.weight(1f, true)) {
                            Spacer(modifier = Modifier.height(it.calculateTopPadding()))
                            currentPreferenceGroup()
                        }
                    }
                }
            }
        }
    
     @Composable
        fun PreferenceGroup(
                title: Int,
                selected: Boolean,
                tablet: Boolean,
                onClick: () -> Unit,
                preferences: @Composable () -> Unit,
                first: Boolean = false,
                icon: (@Composable () -> Unit)? = null
        ) {
            assert(!selected || tablet)
            if (tablet) {
                Box(
                        modifier = Modifier
                            .fillMaxWidth(0.9f)
                            .padding(top = 10.dp, bottom = 10.dp)
                            .height(100.dp)
                            .clip(
                                RoundedCornerShape(12.dp)
                            )
                            .background(
                                if (selected) MaterialTheme.colorScheme.inversePrimary else Color.Transparent
                            )
                            .selectable(selected = selected, onClick = onClick)
                            .padding(start = 8.dp, end = 8.dp),
                        contentAlignment = Alignment.CenterStart
                ) {
                    Layout(content = {
                        icon?.invoke()
                        Text(
                                text = getString(title),
                                style = MaterialTheme.typography.headlineSmall,
                                color = MaterialTheme.colorScheme.onSurfaceVariant,
                           overflow = TextOverflow.Ellipsis,
                    )
                }, modifier = Modifier.fillMaxSize(), measurePolicy = { measurables,
                                                                            constraints ->
                        /*
                        We want to position the text in the center of the box
                        If there is an icon, we want the icon to be left-aligned
                        The icon may require the text to be cut off and/or move to the right
    
                        [ (ic)   text       ]
                        [  text w/out icon  ]
                        [ (ic) long text... ]
                         */
                    if (measurables.isEmpty()) return@Layout layout(0, 0) {
                    }
                    val paddingSize = ICON_PADDING.toPx().toInt()
                    lateinit var text: Placeable
                    var iconPlaceable: Placeable? = null
                    val totalWidth: Int
                    val totalHeight: Int
                    if (measurables.size == 1) {
                        text = measurables[0].measure(constraints)
                        totalWidth = if (constraints.hasBoundedWidth) constraints.maxWidth
                        else text.width
                        totalHeight = if (constraints.hasBoundedHeight) constraints.maxHeight
                        else text.height
                   } else {
                        iconPlaceable = measurables[0].measure(Constraints.fixed(ICON_SIZE.toPx()
                                .toInt(),
                                ICON_SIZE.toPx().toInt()))
                        text = measurables[1].measure(Constraints(
                                0,
                                constraints.maxWidth - iconPlaceable.width - paddingSize,
                                0,
                                constraints.maxHeight))
    
                        totalWidth = if (constraints.hasBoundedWidth) constraints.maxWidth
                        else iconPlaceable.width + paddingSize + text.width
                        totalHeight = if (constraints.hasBoundedHeight) constraints.maxHeight
                        else max(iconPlaceable.height, text.height)
                    }
    
                    layout(totalWidth, totalHeight) {
                        val textX = max((totalWidth - text.width) / 2,
                               (iconPlaceable?.width ?: -paddingSize) + paddingSize)
                        val textY = (totalHeight - text.height) / 2
                        text.place(x = textX, y = textY)
                        iconPlaceable?.place(
                                x = 0,
                                y = (totalHeight - iconPlaceable.height) / 2)
                    }
                })
    
            }
       } else {
            if (!first) Divider(modifier = Modifier.fillMaxWidth(), thickness = 1.dp)
           Text(
                   text = getString(title),
                   style = MaterialTheme.typography.titleSmall,
                   fontWeight = FontWeight.Bold,
                   modifier = Modifier.padding(start = 8.dp, top = LIST_ELEMENT_PADDING, bottom
                   = LIST_ELEMENT_PADDING),
                   overflow = TextOverflow.Ellipsis
            )
            preferences()
        }
    }
    
    @Composable
    fun SplitPreference(
           largePreference: @Composable () -> Unit,
           smallPreference: @Composable () -> Unit,
           modifier: Modifier = Modifier,
    ) {
        Row(
                modifier = modifier
                    .fillMaxWidth()
                    .height(PREFERENCE_HEIGHT),
                verticalAlignment = Alignment.CenterVertically
        ) {
            Box(
                    modifier = modifier
                        .padding(end = SPLIT_PREFERENCE_PADDING)
                        .fillMaxHeight()
                        .weight(1f, fill = true),
                    contentAlignment = Alignment.CenterStart
            ) {
                largePreference()
           }
           Divider(
                    modifier = Modifier
                       .fillMaxHeight(0.6f)
                       .width(1.dp),
                   color = MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.medium)
            )
            Box(
                   modifier = modifier
                       .padding(start = SPLIT_PREFERENCE_PADDING)
                       .size(PREFERENCE_HEIGHT),
                   contentAlignment = Alignment.Center
            ) {
                smallPreference()
            }
        }
    }
    
    @OptIn(ExperimentalAnimationApi::class)
    @Composable
    fun <T> DialoguePreference(
            preference: ComposablePreference<T>,
            title: Int,
            modifier: Modifier = Modifier,
            action: (@Composable (value: T) -> Unit)? = null,
            summary: ((value: T) -> String)? = { value ->
                value.toString()
            },
            icon: (@Composable BoxScope
            .(enabled: Boolean) -> Unit)? = null,
            reserveIconSpace: Boolean = true,
            titleColor: Color = MaterialTheme.colorScheme.onSurface,
            disabledTitleColor: Color = MaterialTheme.colorScheme.onSurface.copy(
                    alpha = ContentAlpha.medium
            ),
            titleStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyLarge,
            summaryColor: Color = MaterialTheme.colorScheme.onSurface.copy(
                    alpha = ContentAlpha.medium
            ),
            summaryStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.labelLarge,
            onPreferenceChanged: ComposablePreferenceChangeListener<T> = object :
                ComposablePreferenceChangeListener<T> {
                override fun onPreferenceChange(
                        preference: ComposablePreference<T>, newValue: T
                ): Boolean {
                    return true
                }
           },
           enabled: Boolean = true,
           default: T? = null,
           dialog: @Composable (
                   preference: ComposablePreference<T>, dismissDialog: () -> Unit, context: Context, title: String
           ) -> Unit
    ) {
        val editing = rememberSaveable {
            mutableStateOf(false)
        }
        AnimatedContent(targetState = editing, transitionSpec = {
            slideIntoContainer(
                    towards = AnimatedContentScope.SlideDirection.Up
            ) with slideOutOfContainer(
                    towards = AnimatedContentScope.SlideDirection.Down
            )
        }) { targetState ->
            if (targetState.value) {
                dialog(preference = preference, dismissDialog = {
                    editing.value = false
                }, context = this@SettingsActivity, title = getString(title))
            }
        }
        Preference(
                preference = preference,
                title = title,
                action = action,
                summary = summary,
                modifier = modifier,
                icon = icon,
                reserveIconSpace = reserveIconSpace,
                titleColor = titleColor,
                disabledTitleColor = disabledTitleColor,
                titleStyle = titleStyle,
                summaryColor = summaryColor,
                summaryStyle = summaryStyle,
                onPreferenceChanged = onPreferenceChanged,
                enabled = enabled,
                onPreferenceClicked = {
                    editing.value = true
                    true
                },
                default = default
        )
    }
    
    @Composable
    fun <T> Preference(
            preference: ComposablePreference<T>,
            title: Int,
            modifier: Modifier = Modifier,
            action: (@Composable (value: T) -> Unit)? = null,
            summary: ((value: T) -> String)? = { value ->
                value.toString()
            },
            icon: (@Composable BoxScope
            .(enabled: Boolean) -> Unit)? = null,
            reserveIconSpace: Boolean = true,
            titleColor: Color = MaterialTheme.colorScheme.onSurface,
            disabledTitleColor: Color = MaterialTheme.colorScheme.onSurface.copy(
                    alpha = ContentAlpha.medium
            ),
            titleStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyLarge,
            summaryColor: Color = MaterialTheme.colorScheme.onSurface.copy(
                    alpha = ContentAlpha.medium
            ),
            summaryStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.labelLarge,
            onPreferenceClicked: (preference: ComposablePreference<T>) -> Boolean = {
                false
            },
            onPreferenceChanged: ComposablePreferenceChangeListener<T> = object :
                    ComposablePreferenceChangeListener<T> {
                override fun onPreferenceChange(
                        preference: ComposablePreference<T>, newValue: T
                ): Boolean {
                    return true
                }
            },
            enabled: Boolean = true,
            default: T? = null
    ) {
        preference.onPreferenceChangeListener.add(onPreferenceChanged)
        val value = if (default != null) preference.getValue(default) else preference.value
        Row(
                modifier = modifier
                    .fillMaxWidth()
                    .height(73.dp)
                    .clickable(enabled = enabled, onClick = {
                        onPreferenceClicked(preference)
                    }),
                verticalAlignment = Alignment.CenterVertically
        ) {
            if (icon != null || reserveIconSpace) {
                val iconSpot: @Composable BoxScope.(enabled: Boolean) -> Unit = icon ?: { }
                Box(
                        modifier = Modifier
                            .padding(ICON_PADDING)
                            .size(ICON_SIZE)
                            .alpha(if (enabled) ContentAlpha.high else ContentAlpha.disabled),
                        contentAlignment = Alignment.Center,
                        content = {
                            iconSpot(enabled)
                        }
                )
            }
            Column(modifier = modifier
                .fillMaxHeight()
                .weight(1f, fill = true),
            verticalArrangement = Arrangement.Center) {
                Text(
                        text = getString(title), style = titleStyle, color = if (enabled) titleColor
                else disabledTitleColor
                )
                if (summary != null) Text(
                        text = summary(preference.value), style = summaryStyle, Color = summaryColor
                )
            }
            if (action != null) Box(
                    modifier = Modifier.size(PREFERENCE_HEIGHT), contentAlignment = Alignment.Center
            ) { action(value) }
        }
    }
    
    Companion object {
        val PREFERENCE_HEIGHT = 73.dp
        val SPLIT_PREFERENCE_PADDING = 5.dp
        val ICON_SIZE = 24.dp
        val ICON_PADDING = 16.dp
        const val FADE_DURATION = 500
    
        private fun grayScaleFilter(): ColorFilter {
            val grayScaleMatrix = ColorMatrix(
                floatArrayOf(
                    0.33f, 0.33f, 0.33f, 0f, 0f,
                    0.33f, 0.33f, 0.33f, 0f, 0f,
                    0.33f, 0.33f, 0.33f, 0f, 0f,
                    0f, 0f, 0f, 1f, 0f
                )
            )
            return ColorFilter.colorMatrix(grayScaleMatrix)
        }
    }
    

    我的字数有限,但我很乐意尝试回答任何问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-27
      • 2014-06-10
      • 1970-01-01
      相关资源
      最近更新 更多