feat: 自定义主题色支持直接输入颜色的十六进制
This commit is contained in:
parent
b18571c1e5
commit
fe0c667af5
|
|
@ -1,6 +1,8 @@
|
||||||
package com.huanchengfly.tieba.post.ui.page.settings.theme
|
package com.huanchengfly.tieba.post.ui.page.settings.theme
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
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.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
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.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.Checkbox
|
import androidx.compose.material.Checkbox
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.OutlinedTextField
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.TextFieldDefaults
|
||||||
import androidx.compose.material.icons.Icons
|
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.Check
|
||||||
import androidx.compose.material.icons.rounded.ColorLens
|
import androidx.compose.material.icons.rounded.ColorLens
|
||||||
import androidx.compose.material.icons.rounded.Colorize
|
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.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
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.stringArrayResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.dp
|
||||||
|
import androidx.core.graphics.toColorInt
|
||||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
import com.github.panpf.sketch.compose.AsyncImage
|
import com.github.panpf.sketch.compose.AsyncImage
|
||||||
import com.github.panpf.sketch.fetch.newFileUri
|
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.ui.widgets.compose.rememberDialogState
|
||||||
import com.huanchengfly.tieba.post.utils.ThemeUtil
|
import com.huanchengfly.tieba.post.utils.ThemeUtil
|
||||||
import com.huanchengfly.tieba.post.utils.appPreferences
|
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.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
|
|
||||||
|
|
@ -119,21 +132,95 @@ fun AppThemePage(
|
||||||
dialogState = customPrimaryColorDialogState,
|
dialogState = customPrimaryColorDialogState,
|
||||||
title = { Text(text = stringResource(id = R.string.title_custom_theme)) },
|
title = { Text(text = stringResource(id = R.string.title_custom_theme)) },
|
||||||
content = {
|
content = {
|
||||||
|
var useInput by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
HarmonyColorPicker(
|
AnimatedContent(
|
||||||
harmonyMode = ColorHarmonyMode.ANALOGOUS,
|
targetState = useInput,
|
||||||
color = HsvColor.from(customPrimaryColor),
|
label = "",
|
||||||
onColorChanged = {
|
modifier = Modifier
|
||||||
customPrimaryColor = it.toColor()
|
.wrapContentHeight()
|
||||||
},
|
.animateContentSize()
|
||||||
modifier = Modifier.size(350.dp)
|
) { 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.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(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.huanchengfly.tieba.post.ui.widgets.compose
|
package com.huanchengfly.tieba.post.ui.widgets.compose
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
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.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
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.text.input.ImeAction
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.R
|
||||||
import com.huanchengfly.tieba.post.arch.BaseComposeActivity.Companion.LocalWindowSizeClass
|
import com.huanchengfly.tieba.post.arch.BaseComposeActivity.Companion.LocalWindowSizeClass
|
||||||
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
||||||
|
|
@ -239,7 +243,6 @@ fun ConfirmDialog(
|
||||||
*
|
*
|
||||||
* @param onValueChange 输入框内容变化时的回调,返回true表示允许变化,false表示不允许变化
|
* @param onValueChange 输入框内容变化时的回调,返回true表示允许变化,false表示不允许变化
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PromptDialog(
|
fun PromptDialog(
|
||||||
onConfirm: (String) -> Unit,
|
onConfirm: (String) -> Unit,
|
||||||
|
|
@ -361,40 +364,72 @@ fun Dialog(
|
||||||
dismissOnClickOutside = cancelableOnTouchOutside
|
dismissOnClickOutside = cancelableOnTouchOutside
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Column(
|
ProvideContentColor(color = ExtendedTheme.colors.text) {
|
||||||
modifier = modifier
|
ConstraintLayout(
|
||||||
.fillMaxWidth(
|
modifier = modifier
|
||||||
fraction = if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {
|
.wrapContentHeight()
|
||||||
1f
|
.animateContentSize()
|
||||||
} else {
|
.fillMaxWidth(
|
||||||
0.6f
|
fraction = if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {
|
||||||
}
|
1f
|
||||||
)
|
} else {
|
||||||
.padding(16.dp)
|
0.6f
|
||||||
.background(
|
}
|
||||||
color = ExtendedTheme.colors.windowBackground,
|
)
|
||||||
shape = RoundedCornerShape(24.dp)
|
.padding(16.dp)
|
||||||
)
|
.background(
|
||||||
.padding(vertical = 24.dp),
|
color = ExtendedTheme.colors.windowBackground,
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
shape = RoundedCornerShape(24.dp)
|
||||||
) {
|
)
|
||||||
ProvideContentColor(color = ExtendedTheme.colors.text) {
|
.padding(vertical = 24.dp),
|
||||||
if (title != null) {
|
) {
|
||||||
Box(
|
val (titleRef, contentRef, buttonsRef) = createRefs()
|
||||||
modifier = Modifier
|
Column(
|
||||||
.padding(horizontal = 24.dp)
|
modifier = Modifier
|
||||||
.align(Alignment.CenterHorizontally)
|
.constrainAs(titleRef) {
|
||||||
) {
|
top.linkTo(parent.top)
|
||||||
ProvideTextStyle(value = MaterialTheme.typography.h6.copy(fontWeight = FontWeight.Bold)) {
|
start.linkTo(parent.start)
|
||||||
dialogScope.title()
|
end.linkTo(parent.end)
|
||||||
|
width = Dimension.fillToConstraints
|
||||||
|
visibility = if (title == null) {
|
||||||
|
Visibility.Gone
|
||||||
|
} else {
|
||||||
|
Visibility.Visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
if (title != null) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.align(Alignment.CenterHorizontally)
|
||||||
|
) {
|
||||||
|
ProvideTextStyle(value = MaterialTheme.typography.h6.copy(fontWeight = FontWeight.Bold)) {
|
||||||
|
dialogScope.title()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()
|
dialogScope.content()
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.constrainAs(buttonsRef) {
|
||||||
|
start.linkTo(parent.start)
|
||||||
|
end.linkTo(parent.end)
|
||||||
|
bottom.linkTo(parent.bottom)
|
||||||
|
width = Dimension.fillToConstraints
|
||||||
|
}
|
||||||
.padding(horizontal = 24.dp),
|
.padding(horizontal = 24.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -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)}"
|
||||||
|
}
|
||||||
|
|
@ -517,4 +517,5 @@
|
||||||
<string name="desc_show">显示</string>
|
<string name="desc_show">显示</string>
|
||||||
<string name="title_settings_status_bar_darker">状态栏遮罩</string>
|
<string name="title_settings_status_bar_darker">状态栏遮罩</string>
|
||||||
<string name="summary_settings_status_bar_darker">开启后非白色主题状态栏将略微变暗</string>
|
<string name="summary_settings_status_bar_darker">开启后非白色主题状态栏将略微变暗</string>
|
||||||
|
<string name="desc_input_color">手动输入颜色</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue