android 可通过Jetpack编写中输入的文本OutlinedTextField进行调整

yv5phkfx  于 2022-11-20  发布在  Android
关注(0)|答案(2)|浏览(234)

我有一个OutlinedTextField,其中包含DropdownMenu,我希望在按下DropdownMenu列表中的项目后,该项目的值开始在OutlinedTextField中,也根据文本的长度通过宽度进行调整。如何做到这一点?

更新(2022年2月14日)

默认情况下,OutlinedTextField***使用***OutlinedTextFieldLayout,其中包含具有*defaultMinSize*修饰符参数BasicTextField

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
            }, ...

通过这些更改,我们不再有默认的宽度,但仍然有一些间距从右边离开

更新(2022年2月15日)

不要从@Compose库复制文件。一些API调用不起作用。在我的例子中,textColor和背景设置对我的自定义OutlinedFormTextField不起作用,而对OutlinedTextField来说,一切都很好:

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

我还发现,您可以用Row组件 Package OutlinedTextField并在其中设置以下内容,而不是覆盖与OutlinedTextField相关的文件:

Modifier.defaultMinSize(minWidth = 1.dp)

它将删除默认情况下Compose建议的巨大minWidth,但标签后的额外间距仍然存在。
有人知道怎么把它取下来吗?

j8ag8udp

j8ag8udp1#

以下是我的临时解决方案,它使用可调整的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
}
deyfvvtc

deyfvvtc2#

要强制BasicTextField扭曲其宽度,而不是使用默认的minWidth = 280 dp,请使用以下修改器:

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

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

相关问题