feat: 自定义主题色支持直接输入颜色的十六进制

This commit is contained in:
HuanCheng65 2024-02-01 18:02:07 +08:00
parent b18571c1e5
commit fe0c667af5
No known key found for this signature in database
GPG Key ID: 5EC9DD60A32C7360
4 changed files with 169 additions and 38 deletions

View File

@ -1,6 +1,8 @@
package com.huanchengfly.tieba.post.ui.page.settings.theme
import android.os.Build
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -15,17 +17,24 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Checkbox
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.BorderColor
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.ColorLens
import androidx.compose.material.icons.rounded.Colorize
@ -34,6 +43,7 @@ import androidx.compose.material.icons.rounded.PhotoSizeSelectActual
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@ -47,7 +57,9 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import androidx.core.graphics.toColorInt
import androidx.datastore.preferences.core.booleanPreferencesKey
import com.github.panpf.sketch.compose.AsyncImage
import com.github.panpf.sketch.fetch.newFileUri
@ -74,6 +86,7 @@ import com.huanchengfly.tieba.post.ui.widgets.compose.TitleCentredToolbar
import com.huanchengfly.tieba.post.ui.widgets.compose.rememberDialogState
import com.huanchengfly.tieba.post.utils.ThemeUtil
import com.huanchengfly.tieba.post.utils.appPreferences
import com.huanchengfly.tieba.post.utils.extension.toHexString
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@ -119,22 +132,96 @@ fun AppThemePage(
dialogState = customPrimaryColorDialogState,
title = { Text(text = stringResource(id = R.string.title_custom_theme)) },
content = {
var useInput by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.padding(horizontal = 16.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
AnimatedContent(
targetState = useInput,
label = "",
modifier = Modifier
.wrapContentHeight()
.animateContentSize()
) { input ->
if (input) {
var inputHexColor by remember { mutableStateOf(customPrimaryColor.toHexString()) }
val lastValidColor by produceState(
initialValue = customPrimaryColor,
inputHexColor
) {
if ("^#([0-9a-fA-F]{6})$".toRegex().matches(inputHexColor)) {
value = Color(inputHexColor.toColorInt())
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxWidth()
) {
Spacer(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.background(lastValidColor)
)
OutlinedTextField(
value = inputHexColor,
onValueChange = {
if ("^#([0-9a-fA-F]{0,6})$".toRegex().matches(it)) {
inputHexColor = it
}
},
maxLines = 1,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
modifier = Modifier.weight(1f),
colors = TextFieldDefaults.outlinedTextFieldColors(
cursorColor = ExtendedTheme.colors.primary,
focusedBorderColor = ExtendedTheme.colors.primary,
focusedLabelColor = ExtendedTheme.colors.primary
)
)
IconButton(
onClick = {
customPrimaryColor = Color(inputHexColor.toColorInt())
useInput = false
}
) {
Icon(
imageVector = Icons.Rounded.Check,
contentDescription = stringResource(id = R.string.button_sure_default)
)
}
}
} else {
Box {
HarmonyColorPicker(
harmonyMode = ColorHarmonyMode.ANALOGOUS,
color = HsvColor.from(customPrimaryColor),
onColorChanged = {
customPrimaryColor = it.toColor()
},
modifier = Modifier.size(350.dp)
modifier = Modifier.sizeIn(maxWidth = 320.dp, maxHeight = 320.dp)
)
IconButton(
onClick = { useInput = true },
modifier = Modifier.align(Alignment.TopEnd)
) {
Icon(
imageVector = Icons.Rounded.BorderColor,
contentDescription = stringResource(id = R.string.desc_input_color)
)
}
}
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),

View File

@ -1,5 +1,6 @@
package com.huanchengfly.tieba.post.ui.widgets.compose
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@ -8,6 +9,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
@ -29,7 +31,6 @@ import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
@ -39,6 +40,9 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.constraintlayout.compose.Visibility
import com.huanchengfly.tieba.post.R
import com.huanchengfly.tieba.post.arch.BaseComposeActivity.Companion.LocalWindowSizeClass
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
@ -239,7 +243,6 @@ fun ConfirmDialog(
*
* @param onValueChange 输入框内容变化时的回调返回true表示允许变化false表示不允许变化
*/
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun PromptDialog(
onConfirm: (String) -> Unit,
@ -361,8 +364,11 @@ fun Dialog(
dismissOnClickOutside = cancelableOnTouchOutside
)
) {
Column(
ProvideContentColor(color = ExtendedTheme.colors.text) {
ConstraintLayout(
modifier = modifier
.wrapContentHeight()
.animateContentSize()
.fillMaxWidth(
fraction = if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {
1f
@ -376,9 +382,22 @@ fun Dialog(
shape = RoundedCornerShape(24.dp)
)
.padding(vertical = 24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
ProvideContentColor(color = ExtendedTheme.colors.text) {
val (titleRef, contentRef, buttonsRef) = createRefs()
Column(
modifier = Modifier
.constrainAs(titleRef) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
width = Dimension.fillToConstraints
visibility = if (title == null) {
Visibility.Gone
} else {
Visibility.Visible
}
}
) {
if (title != null) {
Box(
modifier = Modifier
@ -390,11 +409,27 @@ fun Dialog(
}
}
}
Box(modifier = Modifier.align(Alignment.CenterHorizontally)) {
}
Column(
modifier = Modifier
.constrainAs(contentRef) {
top.linkTo(titleRef.bottom, margin = 16.dp, goneMargin = 0.dp)
bottom.linkTo(buttonsRef.top, margin = 16.dp, goneMargin = 0.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
height = Dimension.preferredWrapContent
}
) {
dialogScope.content()
}
Column(
modifier = Modifier
.constrainAs(buttonsRef) {
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
width = Dimension.fillToConstraints
}
.padding(horizontal = 24.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {

View File

@ -0,0 +1,8 @@
package com.huanchengfly.tieba.post.utils.extension
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
fun Color.toHexString(): String {
return "#${Integer.toHexString(toArgb()).substring(2)}"
}

View File

@ -517,4 +517,5 @@
<string name="desc_show">显示</string>
<string name="title_settings_status_bar_darker">状态栏遮罩</string>
<string name="summary_settings_status_bar_darker">开启后非白色主题状态栏将略微变暗</string>
<string name="desc_input_color">手动输入颜色</string>
</resources>