Skip to Content

Text và Typography trong Jetpack Compose

Hiển thị và xử lý text là một trong những tác vụ phổ biến nhất trong UI. Bài này hướng dẫn chi tiết cách làm việc với Text trong Compose.

1. Text cơ bản

Hiển thị Text đơn giản

@Composable fun BasicTextExamples() { Column { Text("Hello World") Text( text = "Styled Text", color = Color.Blue, fontSize = 20.sp, fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic, letterSpacing = 2.sp ) } }

Các properties của Text

PropertyMô tảVí dụ
textNội dung text"Hello"
colorMàu chữColor.Blue
fontSizeKích thước chữ16.sp
fontWeightĐộ đậmFontWeight.Bold
fontStyleKiểu chữFontStyle.Italic
fontFamilyFont familyFontFamily.Serif
letterSpacingKhoảng cách chữ2.sp
textDecorationUnderline, line-throughTextDecoration.Underline
textAlignCăn lềTextAlign.Center
lineHeightChiều cao dòng24.sp
maxLinesSố dòng tối đa2
overflowXử lý khi trànTextOverflow.Ellipsis

2. TextStyle và Typography

Sử dụng TextStyle

@Composable fun TextStyleExample() { Text( text = "Custom Style", style = TextStyle( color = Color.DarkGray, fontSize = 18.sp, fontWeight = FontWeight.Medium, fontFamily = FontFamily.SansSerif, lineHeight = 24.sp, letterSpacing = 0.5.sp ) ) }

Sử dụng MaterialTheme typography

@Composable fun MaterialTypography() { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text("Display Large", style = MaterialTheme.typography.displayLarge) Text("Headline Medium", style = MaterialTheme.typography.headlineMedium) Text("Title Large", style = MaterialTheme.typography.titleLarge) Text("Body Large", style = MaterialTheme.typography.bodyLarge) Text("Label Medium", style = MaterialTheme.typography.labelMedium) } }

Merge TextStyles

@Composable fun MergedStyle() { val baseStyle = MaterialTheme.typography.bodyLarge Text( text = "Custom merged style", style = baseStyle.copy( color = Color.Blue, fontWeight = FontWeight.Bold ) ) }

3. AnnotatedString - Rich Text

Styled spans

@Composable fun StyledSpans() { Text( buildAnnotatedString { append("Normal text ") withStyle(SpanStyle(color = Color.Red)) { append("Red text ") } withStyle(SpanStyle( fontWeight = FontWeight.Bold, textDecoration = TextDecoration.Underline )) { append("Bold underlined") } } ) }

Paragraph styles

@Composable fun ParagraphStyles() { Text( buildAnnotatedString { withStyle(ParagraphStyle(textAlign = TextAlign.Center)) { append("Centered paragraph\n") } withStyle(ParagraphStyle( textIndent = TextIndent(firstLine = 16.sp), lineHeight = 24.sp )) { append("This paragraph has first-line indentation and custom line height.") } } ) }

ClickableText

@Composable fun ClickableTextExample() { val annotatedString = buildAnnotatedString { append("Click ") pushStringAnnotation(tag = "URL", annotation = "https://google.com") withStyle(SpanStyle(color = Color.Blue, textDecoration = TextDecoration.Underline)) { append("here") } pop() append(" to visit Google") } ClickableText( text = annotatedString, onClick = { offset -> annotatedString.getStringAnnotations(tag = "URL", start = offset, end = offset) .firstOrNull()?.let { annotation -> // Open URL println("Clicked: ${annotation.item}") } } ) }

Sử dụng LinkAnnotation (Compose 1.7+)

@Composable fun ModernLinkExample() { val annotatedString = buildAnnotatedString { append("Read our ") withLink( LinkAnnotation.Url( url = "https://example.com/terms", styles = TextLinkStyles( style = SpanStyle(color = Color.Blue), hoveredStyle = SpanStyle(textDecoration = TextDecoration.Underline) ) ) ) { append("Terms of Service") } } Text(annotatedString) }

5. TextField - Input text

Basic TextField

@Composable fun BasicTextFieldExample() { var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, label = { Text("Enter name") }, placeholder = { Text("John Doe") }, singleLine = true ) }

OutlinedTextField

@Composable fun OutlinedExample() { var email by remember { mutableStateOf("") } var isError by remember { mutableStateOf(false) } OutlinedTextField( value = email, onValueChange = { email = it isError = !it.contains("@") }, label = { Text("Email") }, leadingIcon = { Icon(Icons.Default.Email, null) }, trailingIcon = { if (isError) { Icon(Icons.Default.Warning, null, tint = Color.Red) } }, isError = isError, supportingText = { if (isError) { Text("Invalid email format", color = MaterialTheme.colorScheme.error) } } ) }

Password TextField

@Composable fun PasswordTextField() { var password by remember { mutableStateOf("") } var visible by remember { mutableStateOf(false) } OutlinedTextField( value = password, onValueChange = { password = it }, label = { Text("Password") }, singleLine = true, visualTransformation = if (visible) VisualTransformation.None else PasswordVisualTransformation(), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), trailingIcon = { IconButton(onClick = { visible = !visible }) { Icon( if (visible) Icons.Default.Visibility else Icons.Default.VisibilityOff, null ) } } ) }

6. Keyboard Options và Actions

KeyboardOptions

@Composable fun KeyboardOptionsExample() { var phone by remember { mutableStateOf("") } TextField( value = phone, onValueChange = { phone = it }, label = { Text("Phone") }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Phone, // Bàn phím số điện thoại imeAction = ImeAction.Done // Nút Done ) ) }

Các KeyboardType

TypeMô tả
TextDefault
NumberChỉ số
PhoneSố điện thoại
EmailEmail
PasswordPassword
NumberPasswordPIN
DecimalSố thập phân
UriURL

KeyboardActions

@Composable fun FormFields() { val focusManager = LocalFocusManager.current var name by remember { mutableStateOf("") } var email by remember { mutableStateOf("") } Column { TextField( value = name, onValueChange = { name = it }, label = { Text("Name") }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), keyboardActions = KeyboardActions( onNext = { focusManager.moveFocus(FocusDirection.Down) } ) ) TextField( value = email, onValueChange = { email = it }, label = { Text("Email") }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions( onDone = { focusManager.clearFocus() } ) ) } }

7. Custom Fonts

Thêm font vào resources

Đặt font files vào res/font/:

res/ └── font/ ├── roboto_regular.ttf ├── roboto_bold.ttf └── roboto_italic.ttf

Tạo FontFamily

val RobotoFamily = FontFamily( Font(R.font.roboto_regular, FontWeight.Normal), Font(R.font.roboto_bold, FontWeight.Bold), Font(R.font.roboto_italic, FontWeight.Normal, FontStyle.Italic) ) @Composable fun CustomFontText() { Text( text = "Custom Font", fontFamily = RobotoFamily, fontWeight = FontWeight.Bold ) }

Downloadable Fonts

val provider = GoogleFont.Provider( providerAuthority = "com.google.android.gms.fonts", providerPackage = "com.google.android.gms", certificates = R.array.com_google_android_gms_fonts_certs ) val fontFamily = FontFamily( Font( googleFont = GoogleFont("Lobster"), fontProvider = provider ) )

8. Text Selection

SelectionContainer

@Composable fun SelectableText() { SelectionContainer { Column { Text("This text can be selected") Text("This text can also be selected") } } }

DisableSelection

@Composable fun MixedSelection() { SelectionContainer { Column { Text("Selectable") DisableSelection { Text("Not selectable") } Text("Selectable again") } } }

9. Text overflow và max lines

@Composable fun TextOverflowExamples() { val longText = "This is a very long text that will overflow..." Column { Text( text = longText, maxLines = 1, overflow = TextOverflow.Ellipsis // ... ) Text( text = longText, maxLines = 2, overflow = TextOverflow.Clip // Cắt bỏ ) Text( text = longText, maxLines = 1, overflow = TextOverflow.Visible // Hiển thị tràn ) } }

10. BasicTextField - Custom Design

Khi cần tùy chỉnh hoàn toàn giao diện TextField:

@Composable fun CustomTextField() { var text by remember { mutableStateOf("") } BasicTextField( value = text, onValueChange = { text = it }, modifier = Modifier .fillMaxWidth() .background(Color.LightGray, RoundedCornerShape(8.dp)) .padding(16.dp), textStyle = TextStyle(fontSize = 16.sp), decorationBox = { innerTextField -> Box { if (text.isEmpty()) { Text("Placeholder...", color = Color.Gray) } innerTextField() // Actual text field } } ) }

📝 Tóm tắt

ComponentMục đích
TextHiển thị text
TextFieldInput với Material Design
OutlinedTextFieldInput với outline style
BasicTextFieldInput để custom hoàn toàn
AnnotatedStringRich text với multiple styles
ClickableTextText với click handlers
SelectionContainerCho phép select text

Best Practices

  • Dùng MaterialTheme.typography cho consistency
  • Sử dụng sp cho fontSize (scales with user preferences)
  • Handle keyboard actions cho form fields
  • Provide visual feedback cho TextField errors
  • Dùng BasicTextField khi cần custom design hoàn toàn
Last updated on