我如何在jetpack compose中自定义BasicTextField?

问题描述 投票:0回答:1

我正在努力在我的应用程序中使用 BasicTextField 实现自定义 TextField,但我遇到了三个无法解决的问题。

颜色未应用:尽管设置了颜色,但它们并未按预期反映在 UI 中。

VisualTransformation 不修改文本:我尝试使用自定义的 VisualTransformation 来格式化或屏蔽文本输入,但它没有对文本应用任何转换。

isError 未更改颜色:isError 标志没有像出现错误时那样更新 BasicTextField 的颜色,即使我已将其设置为触发颜色更改。

我尝试过调试这些问题,但我一直无法弄清楚为什么参数没有按预期工作。任何指导或建议将不胜感激

 /**
 * A composable function that represents a styled text field, similar to the input fields in 
 Spotify's UI.
 *
 * @param value The current value of the text field.
 * @param onValueChange A lambda function that handles changes to the text field's value.
 * @param modifier Modifier to customize the layout and styling of the text field.
 * @param trailingIcon An optional composable that adds an icon to the end of the text field 
 (e.g., a clear button or search icon).
 * @param visualTransformation A transformation applied to the text for visual effects, like 
 masking (e.g., password input). Defaults to no transformation.
 * @param keyboardType Specifies the type of keyboard to display (e.g., text, number, email). 
 Defaults to `KeyboardType.Text`.
 * @param imeAction Specifies the action button on the keyboard (e.g., Done, Next). Defaults 
 to `ImeAction.Done`.
 * @param readOnly Boolean that determines whether the text field is read-only. Defaults to 
 `false`.
 */
 @SuppressLint("UnrememberedMutableInteractionSource")
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun SpotifyTextField(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier,
trailingIcon: @Composable() (() -> Unit)? = null,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardType: KeyboardType = KeyboardType.Text,
imeAction: ImeAction = ImeAction.Done,
keyboardActions: KeyboardActions = KeyboardActions.Default,
readOnly: Boolean = false,
isError: Boolean = false
) {
BasicTextField(
    value = value,
    onValueChange = onValueChange,
    modifier = modifier
        .fillMaxWidth()
        .height(48.dp)
 //            .background(
 //                color = MaterialTheme.colorScheme.tertiary,
 //                shape = RoundedCornerShape(4.dp)
 //            )
        .padding(start = 4.dp, end = 4.dp),
    singleLine = true,
    textStyle = MaterialTheme.typography.bodyLarge,
    readOnly = readOnly,
    keyboardOptions = KeyboardOptions(
        keyboardType = keyboardType, imeAction = imeAction,
        hintLocales = LocaleList(Locale("en"))
    ),
    keyboardActions = keyboardActions,
    decorationBox = {innerTextField ->
        TextFieldDefaults.DecorationBox(
            value = value.text,
            isError = isError,
            innerTextField = { innerTextField() },
            visualTransformation = visualTransformation,
            trailingIcon = trailingIcon,
            shape = RoundedCornerShape(4.dp),
            colors = TextFieldDefaults.colors(
                unfocusedContainerColor = MaterialTheme.colorScheme.secondary,
                cursorColor = MaterialTheme.colorScheme.onTertiary,
                focusedTextColor = Color.White,
                unfocusedTextColor = Color.White,
                focusedContainerColor = MaterialTheme.colorScheme.tertiary,
                disabledLabelColor = MaterialTheme.colorScheme.tertiary,
                focusedIndicatorColor = Color.Transparent,
                unfocusedIndicatorColor = Color.Transparent,
                errorTextColor = MaterialTheme.colorScheme.onErrorContainer, //colorResource(id = R.color.brown),
                errorContainerColor = MaterialTheme.colorScheme.errorContainer, //colorResource(id = R.color.white_smoke),
                errorCursorColor = MaterialTheme.colorScheme.error,
                errorTrailingIconColor = MaterialTheme.colorScheme.error,
                errorIndicatorColor = Color.Transparent,
            ),
            contentPadding = PaddingValues(8.dp),
            container = { },
            enabled = true,
            singleLine = true,
           interactionSource = remember { MutableInteractionSource() }
        )
    }
    )
}

 /**
 * A composable function for a password input field with a toggleable visibility icon.
 * The field allows users to show or hide their password by clicking an icon.
 *
 * @param value The current value of the password field.
 * @param onValueChange A lambda function that handles changes to the password field's value.
 */
 @Composable
 fun SpotifyPasswordField(
    modifier: Modifier = Modifier,
    value: TextFieldValue,
    onValueChange: (TextFieldValue) -> Unit,
    imeAction: ImeAction = ImeAction.Done,
    isError: Boolean = false,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
  ) {
    /** showPassword -> A mutable state that determines whether the password should be shown 
(true) or hidden (false).*/
val showPassword = remember { mutableStateOf(false) }
/** Calls the SpotifyTextField composable to create the password field. */
SpotifyTextField(
    modifier = modifier,
    value = value,
    onValueChange = onValueChange,
    isError = isError,
    trailingIcon = {
        /** Displays a toggle button to show or hide the password.*/
        if (showPassword.value) {
            /** When the password is visible, display the "visibility" icon. */
            IconButton(onClick = { showPassword.value = false }) {
                Icon(
                    imageVector = SpotifyIcons.Visibility,
                    contentDescription = "TAG_SHOW_PASSWORD_ICON"
                )
            }
        } else {
            /** When the password is hidden, display the "visibility off" icon. */
            IconButton(onClick = { showPassword.value = true }) {
                Icon(
                    imageVector = SpotifyIcons.VisibilityOff,
                    contentDescription = "TAG_HIDE_PASSWORD_ICON"
                )
            }
        }
    },
    visualTransformation = if (showPassword.value) {
        /** No transformation applied when the password is shown. */
        VisualTransformation.None
    } else {
        /** Applies password masking transformation when the password is hidden. */
        PasswordVisualTransformation()
    },
    imeAction = imeAction,
    keyboardType = KeyboardType.Password,
    keyboardActions = keyboardActions
)
}
android kotlin android-jetpack-compose textfield material3
1个回答
0
投票

如何使用

TextFieldDefaults.DecorationBox
有几个问题。

  1. 您用空 lambda 显式覆盖

    container
    。该容器用于实际布局 DecorationBox 中定义的所有各种元素。如果您没有定义布局,则不会显示任何内容。这就是为什么颜色、
    isError
    以及定义的样式和布局的其余部分被忽略的原因。

    默认容器是

    Container(
        enabled = enabled,
        isError = isError,
        interactionSource = interactionSource,
        modifier = Modifier,
        colors = colors,
        shape = shape,
        focusedIndicatorLineThickness = FocusedIndicatorThickness,
        unfocusedIndicatorLineThickness = UnfocusedIndicatorThickness,
    )
    

    我不知道你为什么要覆盖它,但我建议你简单地删除整个

    container
    参数,以便使用默认值。

  2. 虽然现在显示了大多数颜色,但仍然不考虑 focused 文本字段的特定颜色。原因是装饰盒无法检测到聚焦状态。这是由

    interactionSource
    决定的。正如文档所说:

    您必须首先创建您自己的

    remember
    ed MutableInteractionSource 实例并将其传递到 BasicTextField 以便它调度事件。然后将相同的实例传递给这个装饰框[...]。

    只需向 SpotifyTextField 添加另一个参数即可:

    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    

    然后将此

    interactionSource
    传递给 BasicTextField 以及 DecorationBox。他们都必须使用相同MutableInteractionSource。

  3. 未应用视觉变换的原因是您......好吧,不要应用它。您只需为 DecorationBox 设置它,但要使其可见,您还需要为 BasicTextField 实际设置它。这与上一点类似。一般来说,DecorationBox 的以下参数需要设置为您也用于 BasicTextField 的

    相同值:

    • value
      
      
    • enabled
      
      
    • singleLine
      
      
    • visualTransformation
      
      
    • interactionSource
      
      
    为了强制执行,这些参数不是可选的,因此您

    必须指定它们。他们的文档明确指出,需要将它们设置为用于 BasicTextField 的相同值。

    您需要为 BasicTextField 设置它们,以便它们具有所需的

    功能 您需要为 DecorationBox 设置它们,以便它们可以影响
    样式和布局

将所有内容放在一起,对 BasicTextField 的调用将如下所示:

BasicTextField( value = value, onValueChange = onValueChange, modifier = modifier .fillMaxWidth() .height(48.dp) // .background( // color = MaterialTheme.colorScheme.tertiary, // shape = RoundedCornerShape(4.dp) // ) .padding(start = 4.dp, end = 4.dp), singleLine = true, textStyle = MaterialTheme.typography.bodyLarge, readOnly = readOnly, keyboardOptions = KeyboardOptions( keyboardType = keyboardType, imeAction = imeAction, hintLocales = LocaleList(Locale("en")), ), keyboardActions = keyboardActions, visualTransformation = visualTransformation, interactionSource = interactionSource, decorationBox = { innerTextField -> TextFieldDefaults.DecorationBox( value = value.text, isError = isError, innerTextField = innerTextField, visualTransformation = visualTransformation, trailingIcon = trailingIcon, shape = RoundedCornerShape(4.dp), colors = TextFieldDefaults.colors( unfocusedContainerColor = MaterialTheme.colorScheme.secondary, cursorColor = MaterialTheme.colorScheme.onTertiary, focusedTextColor = Color.White, unfocusedTextColor = Color.White, focusedContainerColor = MaterialTheme.colorScheme.tertiary, disabledLabelColor = MaterialTheme.colorScheme.tertiary, focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent, errorTextColor = MaterialTheme.colorScheme.onErrorContainer, //colorResource(id = R.color.brown), errorContainerColor = MaterialTheme.colorScheme.errorContainer, //colorResource(id = R.color.white_smoke), errorCursorColor = MaterialTheme.colorScheme.error, errorTrailingIconColor = MaterialTheme.colorScheme.error, errorIndicatorColor = Color.Transparent, ), contentPadding = PaddingValues(8.dp), enabled = true, singleLine = true, interactionSource = interactionSource, ) }, )
    
© www.soinside.com 2019 - 2024. All rights reserved.