Jetpack Compose中的基本组件-文本输入框

前言

文本的输入框架本身提供了

  • BasicTextField

  • TextField

  • OutlinedTextField

BasicTextField是最基本的输入框,没有什么样式,也方便我们自定义,是我们最常用的组件。

后两者有自带的样式和交互效果,但是实际项目中并不符合我们的效果,所以一般很少用。

BasicTextField

基础示例

1
2
3
4
BasicTextField(
value = "",
onValueChange = {},
)

密码输入

1
2
3
4
5
6
7
8
BasicTextField(
value = text,
visualTransformation = if (inputVisible) {
VisualTransformation.None
} else {
PasswordVisualTransformation()
}
)

其中

  • visualTransformation = VisualTransformation.None 显示输入字符
  • visualTransformation = PasswordVisualTransformation() 输入字符显示为密文形式

文本渐变和光标渐变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Composable  // @Composable 注解表示该函数可以作为可组合项使用
fun GradientTextField() { // GradientTextField 函数用于创建一个带有渐变效果的文本输入框
var text by remember { mutableStateOf("") } // mutableStateOf 创建一个可变状态
BasicTextField( // BasicTextField 是一个基本的文本输入框
value = text,
onValueChange = { text = it },
textStyle = TextStyle( // textStyle 设置文本的样式
brush = Brush.linearGradient( // linearGradient 设置线性渐变
colors = listOf(Color.Red, Color.Blue, Color.Green, Color.Magenta)
),
fontSize = 32.sp
),
cursorBrush = Brush.verticalGradient( // verticalGradient 设置垂直渐变
colors = listOf(Color.Blue, Color.Cyan, Color.Red, Color.Magenta)
),
)
}

placeholder

默认是不支持placeholder的,我们可以自定义实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

@Composable
fun BasicTextFieldWithPlaceholder(
fontSize: Int,
placeholder: String,
onFocusChange: (focus: Boolean) -> Unit,
onValueChange: (String) -> Unit
) {
var text by remember { mutableStateOf("") }

BasicTextField(
value = text,
modifier = Modifier
.fillMaxSize()
.focusable()
.onFocusChanged { focusState ->
onFocusChange(focusState.isFocused)
},
onValueChange = { text = it;onValueChange(it) },
singleLine = true,
textStyle = TextStyle(
color = Color(0xffeeeeee),
fontSize = fontSize.sp
),
decorationBox = { innerTextField ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(start = 8.dp, end = 8.dp)
) {
if (text.isBlank()) {
Text(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
text = placeholder,
style = TextStyle(color = Color.White),
fontSize = fontSize.sp
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center)
) {
innerTextField()
}
}
},
cursorBrush = SolidColor(Color.White),
)
}

TextField

1
2
3
4
5
6
7
8
9
@Composable
fun SimpleFilledTextFieldSample() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("姓名") }
)
}

输入类型为密码

1
2
3
4
5
6
7
8
9
10
11
12
@Composable
fun PasswordTextField() {
var password by rememberSaveable { mutableStateOf("") }

TextField(
value = password,
onValueChange = { password = it },
label = { Text("Enter password") },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
)
}

彩虹色文字

1
2
3
4
5
6
7
8
9
10
11
12
@Composable
fun SimpleFilledTextFieldSample() {
var text by remember { mutableStateOf("") }
val brush = remember {
Brush.linearGradient(
colors = listOf<Color>(Color.Red, Color.Green, Color.Blue)
)
}
TextField(
value = text, onValueChange = { text = it }, textStyle = TextStyle(brush = brush)
)
}

OutlinedTextField

1
2
3
4
5
6
7
8
9
10
@Composable
fun SimpleOutlinedTextFieldSample() {
var text by remember { mutableStateOf("") }

OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("Label") }
)
}

去除涟漪效果

只要组件设置的可点击,就会出现涟漪效果,要想去除涟漪效果,我们可以扩展Modifier的方法来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import android.annotation.SuppressLint
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import kotlinx.coroutines.delay

inline fun Modifier.noRippleClickable(crossinline onClick: () -> Unit): Modifier = composed {
clickable(indication = null,
interactionSource = remember { MutableInteractionSource() }) {
onClick()
}
}

使用方法:

clickable更换为noRippleClickable即可。

两种组件封装形式

在对 BasicTextField 进行组件封装时,既可以使用 MutableState 也可以使用回调来在外层获取组件内的值。

下面分别介绍这两种方式的特点和使用场景。

使用 MutableState

原理

通过将 MutableState 对象作为参数传递给封装的 BasicTextField 组件,组件内部直接操作这个 MutableState 的值,由于 MutableState 是引用类型,所以在组件外部也能感知到其值的变化。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

@Composable
fun CustomBasicTextField(textState: MutableState<String>) {
var text by textState
BasicTextField(
value = text,
onValueChange = { newText ->
text = newText
}
)
}

// 使用示例
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.window.singleWindowApplication

fun main() = singleWindowApplication {
val textState = mutableStateOf("")
CustomBasicTextField(textState)
// 可以直接访问 textState 的值
val currentText = textState.value
println("当前文本值: $currentText")
}

优缺点

优点

  • 代码简洁:使用 MutableState 可以减少回调函数的使用,使代码逻辑更加清晰。
  • 数据流向明确:直接操作状态对象,数据的流动方向一目了然,便于理解和维护。

缺点

  • 耦合度较高:组件与外部的状态紧密绑定,降低了组件的独立性和可复用性。

使用回调

原理

定义一个回调函数作为参数传递给封装的 BasicTextField 组件,当组件内部的值发生变化时,调用这个回调函数并将新的值作为参数传递出去。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue

@Composable
fun CustomBasicTextField(onTextChanged: (String) -> Unit) {
var text by mutableStateOf("")
BasicTextField(
value = text,
onValueChange = { newText ->
text = newText
onTextChanged(newText)
}
)
}

// 使用示例
import androidx.compose.ui.window.singleWindowApplication

fun main() = singleWindowApplication {
var currentText by mutableStateOf("")
CustomBasicTextField { newText ->
currentText = newText
println("当前文本值: $currentText")
}
}

优缺点

优点

  • 解耦性强:组件与外部的耦合度较低,组件只负责触发回调,不关心外部如何处理数据,提高了组件的可复用性。
  • 灵活性高:可以根据需要在回调函数中添加更多的逻辑,比如数据验证、格式化等。

缺点

  • 代码复杂度增加:需要定义和管理回调函数,增加了代码的复杂度。

选择建议

  • 使用 MutableState 的场景:当组件与外部的交互比较简单,且数据流向比较明确时,使用 MutableState 可以使代码更加简洁。

    例如,组件只是简单地修改一个状态值,而不需要进行额外的逻辑处理。

  • 使用回调的场景:当组件需要与外部进行复杂的交互,或者需要在值变化时执行一些额外的逻辑时,使用回调可以提高组件的灵活性和可复用性。

    例如,在值变化时需要进行数据验证、发送网络请求等操作。