Compose TextField详解

首先明确Compose TextField的底层依赖是:

TextField \rightarrow BasicTextField \rightarrow CoreTextField

相较于Text,TextField需要提供可输入的能力以及渲染效果动态更新的能力。 

  // CompositionLocals
    // If the text field is disabled or read-only, we should not deal with the input service

// !!!用于请求焦点的对象。通过调用 focusRequester.requestFocus(),可以请求将焦点设置到与之关联的 TextField 上,但如何设置的???
    val textInputService = LocalTextInputService.current

// 输入框显示的内容的密度
    val density = LocalDensity.current

// 字体的管理
    val fontFamilyResolver = LocalFontFamilyResolver.current


    val selectionBackgroundColor = LocalTextSelectionColors.current.backgroundColor


// 管理焦点的对象
    val focusManager = LocalFocusManager.current

// 获取当前窗口的信息。窗口是什么???
    val windowInfo = LocalWindowInfo.current

// 用于控制键盘的对象
    val keyboardController = LocalSoftwareKeyboardController.current

针对所谓的文本内容超过TextField的显示区域,会出现Scroller滚动条来动态查看所输入的内容;默认情况下,次效果不显示;且Scroller不算TextField的核心的内容。

 // Scroll state
    val singleLine = maxLines == 1 && !softWrap && imeOptions.singleLine
    val orientation = if (singleLine) Orientation.Horizontal else Orientation.Vertical
    val scrollerPosition = textScrollerPosition ?: rememberSaveable(
        orientation,
        saver = TextFieldScrollerPosition.Saver
    ) { TextFieldScrollerPosition(orientation) }
    if (scrollerPosition.orientation != orientation) {
        throw IllegalArgumentException(
            "Mismatching scroller orientation; " + (
                if (orientation == Orientation.Vertical)
                    "only single-line, non-wrap text fields can scroll horizontally"
                else
                    "single-line, non-wrap text fields can only scroll horizontally"
                )
        )
    }

代码的作用是创建和初始化一个 TextFieldState 对象,并更新其属性。首先通过currentRecomposeScope获取当前重新组合的作用域(recompose scope)。

使用 remember 函数创建一个 TextFieldState 对象,并将其赋值给变量 state;其中keyboardController 是一个键盘控制器对象,它被传递给 remember 函数作为键,以确保在重新组合时 TextFieldState 对象可与输入内容保持一致。

state.update 方法,更新 TextFieldState 对象的属性。这些属性包括文本内容、样式、软换行、密度等,以及一些回调函数和其他对象。通过更新这些属性,可以确保 TextFieldState 对象的状态与传递的参数保持一致。

    val scope = currentRecomposeScope
    // 产生一个关键的对象 state
    val state = remember(keyboardController) {
        TextFieldState(
            TextDelegate(
                text = visualText,
                style = textStyle,
                softWrap = softWrap,
                density = density,
                fontFamilyResolver = fontFamilyResolver
            ),
            recomposeScope = scope,
            keyboardController = keyboardController
        )
    }
    state.update(
        value.annotatedString,
        visualText,
        textStyle,
        softWrap,
        density,
        fontFamilyResolver,
        onValueChange,
        keyboardActions,
        focusManager,
        selectionBackgroundColor
    )

TextFieldState是CoreTextField完成构建各类组件的对象以及声明剩余输入框样式及内容的状态state之后开始构建支持TextField样式渲染的各种Modifier。以此可以理解,Compose布局的过程是先测量到布局到渲染,所以构建各类对象再构建各种Modifier的顺序理应如此。

// Focus
    val focusModifier = Modifier.textFieldFocusModifier(
        enabled = enabled,
        focusRequester = focusRequester,
        interactionSource = interactionSource
    ) {
        if (state.hasFocus == it.isFocused) {
            return@textFieldFocusModifier
        }
        state.hasFocus = it.isFocused


        if (textInputService != null) {
            if (state.hasFocus && enabled && !readOnly) {
                startInputSession(
                    textInputService,
                    state,
                    value,
                    imeOptions,
                    offsetMapping
                )
            } else {
                endInputSession(state)
            }


            // The focusable modifier itself will request the entire focusable be brought into view
            // when it gains focus – in this case, that's the decoration box. However, since text
            // fields may have their own internal scrolling, and the decoration box can do anything,
            // we also need to specifically request that the cursor itself be brought into view.
            // TODO(b/216790855) If this request happens after the focusable's request, the field
            //  will only be scrolled far enough to show the cursor, _not_ the entire decoration
            //  box.
            if (it.isFocused) {
                state.layoutResult?.let { layoutResult ->
                    coroutineScope.launch {
                        bringIntoViewRequester.bringSelectionEndIntoView(
                            value,
                            state.textDelegate,
                            layoutResult.value,
                            offsetMapping
                        )
                    }
                }
            }
        }
        if (!it.isFocused) manager.deselect()
    }

1️⃣ 为 TextField 添加一个修饰符(modifier):focusModifier,用于处理焦点相关的逻辑。

接下来,继续执行多个if语句,旨在判断是否提供可交互的TextField输入框组件。如果存在 textInputService(文本输入服务),并且 TextFieldState 对象具有焦点、启用且不是只读模式,那么会调用 startInputSession 函数来启动输入会话。这个函数会与文本输入服务进行交互,以处理文本输入和键盘事件。如果 TextFieldState 对象失去焦点,会调用 endInputSession 函数来结束输入会话。

coroutineScope.launchstartInputSession 函数分别用于在协程中异步执行将光标位置带入视图的请求和启动输入会话的操作。

因此focusModifier可以总结出必须具备这样的参数条件,页面中的TextField组件才是可输入进行交互的:

  1. 存在 textInputService(文本输入服务)。
  2. TextFieldState 对象具有焦点(state.hasFocus 为 true)。
  3. TextField 启用(enabled 为 true)。
  4. TextField 不是只读模式(readOnly 为 false)。
val pointerModifier = Modifier.textFieldPointer(
        manager,
        enabled,
        interactionSource,
        state,
        focusRequester,
        readOnly,
        offsetMapping
    )

 2️⃣ pointerModifier 是一个修饰符,用于处理与指针(例如鼠标)交互相关的逻辑。

它接受多个参数,包括 managerenabledinteractionSourcestatefocusRequesterreadOnlyoffsetMapping

这个修饰符的作用是为 TextField 添加指针交互的功能。它处理与指针相关的事件,例如鼠标点击、拖动等。通过使用这个修饰符,可以实现对 TextField 的指针交互进行控制和处理。

3️⃣ drawModifier 是一个修饰符,用于在 TextField 的绘制过程中进行自定义绘制操作。

这个修饰符使用了 drawBehind 函数,允许在组件的背后进行绘制操作。调用drawIntoCanvas 函数,获取 Canvas 对象,然后在这个 Canvas 上进行绘制操作。canvasvalueoffsetMappinglayoutResult.valuestate.selectionPaint。这些参数用于确定绘制的位置、内容和样式。

所以我们可以明确TextField不仅是会基于ResumableComposeNode作为渲染的基础,而且还是基于canvas进行渲染的。

使用Jetpack ComposeTextField控件可以通过设置一个限制器(`VisualTransformation`)来限制输入的字数。下面是一个示例代码: ```kotlin import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardType import androidx.compose.material.TextField import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview @Composable fun LimitedTextField(maxLength: Int, value: String, onValueChange: (String) -> Unit) { var text by remember { mutableStateOf(value) } TextField( value = text, onValueChange = { if (it.length <= maxLength) { text = it onValueChange(it) } }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Text ), visualTransformation = object : VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { return if (text.text.length <= maxLength) { TransformedText(text) } else { TransformedText(AnnotatedString(text.text.substring(0, maxLength))) } } } ) } @Preview @Composable fun LimitedTextFieldPreview() { LimitedTextField(maxLength = 10, value = "", onValueChange = {}) } ``` 这里的`LimitedTextField`函数是一个自定义的组件,它接受`maxLength`、`value`和`onValueChange`作为参数。在`TextField`的`onValueChange`回调中,我们检查输入的文本长度是否小于等于`maxLength`,如果是,则更新文本并调用`onValueChange`回调函数。同时,我们还使用`visualTransformation`来限制输入的文本长度,超过限制时会截断文本。 你可以通过调用`LimitedTextField`函数来创建一个限制字数的文本输入框。例如,`LimitedTextField(maxLength = 20, value = "", onValueChange = { /* 处理输入的文本 */ })`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值