【问题标题】:Adjustable by entered text OutlinedTextField in Jetpack Compose可通过 Jetpack Compose 中输入的文本 OutlinedTextField 进行调整
【发布时间】:2022-11-20 06:08:10
【问题描述】:

我有一个带有 DropdownMenu 的 OutlinedTextField 我希望在按下 DropdownMenu 列表内的项目后,项目的值开始位于 OutlinedTextField 内,并根据文本的长度调整宽度。 我该怎么做?

更新 (14.02.2022)

默认大纲文本域使用OutlinedTextField 布局其中包含基本文本域默认最小尺寸修饰符参数。

BasicTextField(
    value = value,
    modifier = modifier
        .then(
            if (decoratedLabel != null) {
                Modifier.padding(top = OutlinedTextFieldTopPadding)
            } else {
                Modifier
            }
        )
        .defaultMinSize(
            minWidth = MinWidth,
            minHeight = MinHeight
        )
...

 /** The default min width applied for a TextField and OutlinedTextField. Note that you can override it by applying Modifier.widthIn directly on a text field. */
 val MinWidth = 280.dp

要使宽度为 Intrinsic Min,我必须从 Compose 库中复制 3 个文件(TextField.kt、TextFieldImpl.kt、OutlinedTextField.kt),并使用 OutlinedTextFieldLayout 组件中的这些更改制作我自己的 OutlinedTextField:

 @Composable
internal fun OutlinedFormTextFieldLayout(
    modifier: Modifier,
    value: TextFieldValue,
    onValueChange: (TextFieldValue) -> Unit,
    enabled: Boolean,
    readOnly: Boolean,
    keyboardOptions: KeyboardOptions,
    keyboardActions: KeyboardActions,
    textStyle: TextStyle,
    singleLine: Boolean,
    maxLines: Int = Int.MAX_VALUE,
    visualTransformation: VisualTransformation,
    interactionSource: MutableInteractionSource,
    decoratedPlaceholder: @Composable ((Modifier) -> Unit)?,
    decoratedLabel: @Composable (() -> Unit)?,
    leading: @Composable (() -> Unit)?,
    trailing: @Composable (() -> Unit)?,
    leadingColor: Color,
    trailingColor: Color,
    labelProgress: Float,
    indicatorWidth: Dp,
    indicatorColor: Color,
    cursorColor: Color,
    shape: Shape
) {
    val textWidth = remember { mutableStateOf(0) }
    val labelSize = remember { mutableStateOf(Size.Zero) }

    fun Modifier.widthIntrinsicSizeMinModifier() = width(IntrinsicSize.Min)
    fun Modifier.widthTextWidthModifier() = width(textWidth.value.dp)

    if (textWidth.value == 0) {
        modifier.then(Modifier.widthIntrinsicSizeMinModifier())
    } else {
        modifier.then(Modifier.widthTextWidthModifier())
    }

    BasicTextField(
        value = value,
        modifier = modifier
            .then(
                if (decoratedLabel != null) {
                    Modifier.padding(top = OutlinedTextFieldTopPadding)
                } else {
                    Modifier
                }
            )
            .onSizeChanged {
                textWidth.value = it.width
            }, ...

通过这些更改,我们不再有默认宽度,但我们仍然从右侧留出一些间距

更新(15.02.2022)

不要从@Compose 库中复制文件。某些 API 调用将不起作用。 在我的例子中,textColor 和背景设置不适用于我的自定义 OutlinedFormTextField,其中 OutlinedTextField 一切正常:

  colors = TextFieldDefaults.outlinedTextFieldColors(
       textColor = Color.Red,
       backgroundColor = backgroundColor.value
...

我还发现,与其以某种方式覆盖与 OutlinedTextField 相关的文件,不如用 Row 组件包装 OutlinedTextField 并在其中设置:

Modifier.defaultMinSize(minWidth = 1.dp)

它将删除 Compose 默认建议的巨大 minWidth,但标签后的额外间距仍然存在。

有谁知道如何删除它?

【问题讨论】:

    标签: android android-jetpack-compose


    【解决方案1】:

    这是我使用可调整的 OutlinedTextField 组件的临时解决方案。 它还修复了背景更改时左上角的 OutlinedTextField 交叉标签:

    // Duration
    private const val LabelOffsetAnimationDuration = 500
    private const val LabelBackgroundColorAnimationDuration = 1
    
    private const val LabelBackgroundColorAnimationDelay = 200
    
    private const val TextFieldBackgroundColorAnimationDuration = 0
    private const val TextFieldBackgroundColorAnimationDelay = 0
    
    private const val BorderColorAnimationDuration = 10
    private const val BorderColorAnimationDelay = 50
    
    // Offset
    private const val LabelAnimationTopLeftPositionOffsetX = 20F
    private const val LabelAnimationTopLeftPositionOffsetY = -30F
    private const val LabelAnimationCenterPositionOffsetX = 25F
    private const val LabelAnimationCenterPositionOffsetY = 0F
    
    private const val LabelTextSizeAnimationTopLeftPosition = 12F
    private const val LabelTextSizeAnimationCenterPosition = 16F
    
    // Z-Index
    private const val LabelBubbleZIndex = 2f
    
    // Size
    private val TextFieldMinWidth = 100.dp
    
    private fun getTargetLabelPosition(isFocused: Boolean, text: String) =
     if (!isFocused && text.isNotEmpty() || isFocused) {
         LabelPosition.TopLeft
     } else {
         LabelPosition.Center
     }
    
    @Composable
    fun MyOutlinedTextField(
        modifier: Modifier = Modifier,
        textFieldState: TextFieldState,
        onValueChange: (String) -> Unit,
        label: String,
        enabled: Boolean = true,
        readOnly: Boolean = false,
        isError: Boolean,
        keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
        keyboardAction: ((KeyboardActionScope) -> Unit),
        trailingIcon: @Composable (() -> Unit)? = null,
    ) {
        Column(
            modifier = modifier,
            verticalArrangement = Arrangement.Center
        ) {
            var textFieldIsFocused by remember { mutableStateOf(false) }
            var labelPosition by remember {
                mutableStateOf(
                    getTargetLabelPosition(
                        isFocused = textFieldIsFocused,
                        text = textFieldState.text
                    )
                )
            }
    
            val labelPositionTransition = updateTransition(
                targetState = labelPosition,
                label = LabelPositionTransitionLabel
            )
    
            val labelOffsetAnimation by labelPositionTransition.animateOffset(
                transitionSpec = {
                    tween(
                        durationMillis = LabelOffsetAnimationDuration
                    )
                },
                label = LabelOffsetAnimationLabel
            ) { position ->
                when (position) {
                    LabelPosition.TopLeft -> Offset(
                        LabelAnimationTopLeftPositionOffsetX,
                        LabelAnimationTopLeftPositionOffsetY
                    )
                    LabelPosition.Center -> Offset(
                        LabelAnimationCenterPositionOffsetX,
                        LabelAnimationCenterPositionOffsetY
                    )
                }
            }
    
            val labelTextSizeAnimation by labelPositionTransition.animateFloat(
                label = LabelTextSizeAnimationLabel
            ) { position ->
                when (position) {
                    LabelPosition.TopLeft -> LabelTextSizeAnimationTopLeftPosition
                    LabelPosition.Center -> LabelTextSizeAnimationCenterPosition
                }
            }
    
            val labelBackgroundColorAnimation by labelPositionTransition.animateColor(
                transitionSpec = {
                    tween(
                        durationMillis = LabelBackgroundColorAnimationDuration,
                        delayMillis = LabelBackgroundColorAnimationDelay,
                    )
                },
                label = LabelBackgroundColorAnimationLabel
            ) { position ->
                when (position) {
                    LabelPosition.TopLeft -> Color.White
                    LabelPosition.Center -> Color.Transparent
                }
            }
    
            val textFieldIsFocusedTransition = updateTransition(
                targetState = textFieldIsFocused,
                label = TextFieldIsFocusedTransitionLabel
            )
    
            val textFieldBackgroundColorAnimation by textFieldIsFocusedTransition.animateColor(
                transitionSpec = { ->
                    tween(
                        durationMillis = TextFieldBackgroundColorAnimationDuration,
                        delayMillis = TextFieldBackgroundColorAnimationDelay,
                    )
                },
                label = TextFieldBackgroundColorAnimationLabel
            ) { _isFocused ->
                when {
                    _isFocused -> LinkWater
                    !_isFocused && textFieldState.text.isEmpty() -> Color.Transparent
                    !_isFocused && textFieldState.text.isNotEmpty() -> Alabaster
                    else -> Color.Transparent
                }
            }
    
            val borderColorAnimation by textFieldIsFocusedTransition.animateColor(
                transitionSpec = { ->
                    tween(
                        durationMillis = BorderColorAnimationDuration,
                        delayMillis = BorderColorAnimationDelay,
                    )
                },
                label = BorderColorAnimationLabel
            ) { _isFocused ->
                when {
                    _isFocused -> Color.Transparent
                    !_isFocused && textFieldState.text.isEmpty() -> Mercury
                    !_isFocused && textFieldState.text.isNotEmpty() -> Color.Transparent
                    else -> Mercury
                }
            }
    
            val textFieldBoxModifier = Modifier.textFieldBoxModifier(
                textFieldState = textFieldState,
                textFieldBackgroundColorAnimation = textFieldBackgroundColorAnimation,
                borderColorAnimation = borderColorAnimation,
                textFieldIsFocused = textFieldIsFocused
            )
    
            Box(
                contentAlignment = Alignment.CenterStart
            ) {
                Text(
                    modifier = Modifier
                        .zIndex(LabelBubbleZIndex)
                        .defaultMinSize(1.dp)
                        .offset(labelOffsetAnimation.x.dp, labelOffsetAnimation.y.dp)
                        .clip(
                            shape = CircleShape
                        )
                        .background(
                            color = labelBackgroundColorAnimation
                        )
                        // Padding inside the Email bubble
                        .padding(
                            start = if (labelPosition == LabelPosition.TopLeft) 8.dp else 0.dp,
                            end = if (labelPosition == LabelPosition.TopLeft) 8.dp else 0.dp,
                            top = 2.dp,
                            bottom = 2.dp
                        ),
                    text = label,
                    fontSize = dpToSp(
                        labelTextSizeAnimation,
                        LocalContext.current
                    ).sp,
                    style = LocalTextStyle.current.copy(
                        color = OsloGray
                    ),
                )
    
                Box(
                    modifier = textFieldBoxModifier
                ) {
                    TextField(
                        modifier = Modifier
                            .padding(
                                start = 8.dp,
                                end = 8.dp
                            )
                            .onFocusChanged {
                                textFieldIsFocused = it.isFocused
                                labelPosition =
                                    getTargetLabelPosition(textFieldIsFocused, textFieldState.text)
                                textFieldState.enableDisplayErrors(textFieldIsFocused)
                            }
                            .defaultMinSize(
                                minWidth = 1.dp
                            ),
                        value = textFieldState.text,
                        onValueChange = onValueChange,
                        enabled = enabled,
                        keyboardOptions = keyboardOptions,
                        readOnly = readOnly,
                        isError = isError,
                        keyboardActions = KeyboardActions(
                            keyboardAction
                        ),
                        trailingIcon = trailingIcon,
                        singleLine = true,
                        textStyle = TextFieldStyle,
                        colors = TextFieldDefaults.textFieldColors(
                            textColor = Fiord,
                            cursorColor = Fiord,
                            disabledTextColor = Color.Transparent,
                            backgroundColor = Color.Transparent,
                            focusedIndicatorColor = Color.Transparent,
                            unfocusedIndicatorColor = Color.Transparent,
                            disabledIndicatorColor = Color.Transparent,
                            errorIndicatorColor = Color.Transparent
                        )
                    )
                }
            }
            textFieldState.getError()?.let { error ->
                TextFieldError(
                    textError = error
                )
            }
        }
    }
    
    private fun Modifier.textFieldBoxModifier(
        textFieldState: TextFieldState,
        textFieldBackgroundColorAnimation: Color,
        borderColorAnimation: Color,
        textFieldIsFocused: Boolean
    ): Modifier {
        var basicTextFieldBoxModifier = this
            .padding(
                start = 4.dp,
                end = 4.dp,
                top = 8.dp,
                bottom = 8.dp
            )
            .height(60.dp)
            .background(
                color = textFieldBackgroundColorAnimation,
                shape = CircleShape
            )
            .border(1.dp, borderColorAnimation, CircleShape)
            .zIndex(1f)
            .wrapContentSize(
                align = Alignment.CenterStart
            )
    
        when {
            textFieldIsFocused -> {
                basicTextFieldBoxModifier = basicTextFieldBoxModifier.then(Modifier.fillMaxWidth())
            }
            !textFieldIsFocused && textFieldState.text.isEmpty() -> {
                basicTextFieldBoxModifier =
                    basicTextFieldBoxModifier.then(Modifier.width(TextFieldMinWidth))
            }
        }
    
        return basicTextFieldBoxModifier
    }
    

    【讨论】:

      【解决方案2】:

      要强制 BasicTextField 扭曲其宽度而不是使用默认的最小宽度 = 280 dp,请使用以下修饰符:

      BasicTextField(
          modifier = modifier
            .width(IntrinsicSize.Min)
      )
      

      什么不起作用:设置Modifier.widthIn(min = 1.dp)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-09
        • 2021-10-01
        • 2021-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多