https://developer.android.com/develop/ui/compose/text
Compose 텍스트 처리
기본 | Material 고수준 | |
---|---|---|
텍스트 | BasicText | Text |
텍스트 입력 | BasicTextField | TextField |
@Composable
fun SimpleText() {
// 기본 텍스트
Text("Hello World")
// Resource에서 가져오기
Text(stringResource(R.string.hello_world))
}
@Composable
fun BlueText() {
// 색상
Text("Hello World", color = Color.Blue)
// 크기
Text("Hello World", fontSize = 30.sp)
// 스타일
Text("Hello World", fontStyle = FontStyle.Italic)
// Bold 및 굵기
Text("Hello World", fontWeight = FontWeight.Bold)
}
TextStyle을 사용하여 대응
@Composable
fun TextShadow() {
val offset = Offset(5.0f, 10.0f)
Text(
text = "Hello world!",
style = TextStyle(
fontSize = 24.sp,
shadow = Shadow(
color = Color.Blue, offset = offset, blurRadius = 3f
)
)
)
}
AnnotatedString을 사용하여 여러 스타일을 설정 가능
buildAnnotatedString DSL을 사용해서도 쉽게 사용
@Composable
fun MultipleStylesInText() {
Text(
buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Blue)) {
append("H")
}
append("ello ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Red)) {
append("W")
}
append("orld")
}
)
}
Brush API를 TextStyle 및 SpanStyle와 함께 사용
TextStyle의 Brush API는 Experimental API
TextStyle에 Brush 사용하여 표현 가능
val gradientColors = listOf(Cyan, LightBlue, Purple /*...*/)
Text(
text = text,
style = TextStyle(
brush = Brush.linearGradient(
colors = gradientColors
)
)
)
Text(
text = buildAnnotatedString {
append("Do not allow people to dim your shine\n")
withStyle(
SpanStyle(
brush = Brush.linearGradient(
colors = rainbowColors
)
)
) {
append("because they are blinded.")
}
append("\nTell them to put some sunglasses on.")
}
)
Text에 marquee 효과 적용
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BasicMarqueeSample() {
// Marquee는 컨테츠가 최대 너비에 맞지 않는 경우에만 애니메이션이 적용
Column(Modifier.width(400.dp)) {
Text(
"Learn about why it's great to use Jetpack Compose",
modifier = Modifier.basicMarquee(),
fontSize = 50.sp
)
}
}
Text의 textAlign
파라미터를 사용해서 텍스트의 가로 정렬 가능
TextAlign.Start
/TextAlign.End
을 권장@Composable
fun CenterText() {
Text(
"Hello World", textAlign = TextAlign.Center, modifier = Modifier.width(150.dp)
)
}
단락에 여러 스타일을 추가하려면 AnnotatedString에서 ParagraphStyle를 사용
@Composable
fun ParagraphStyle() {
Text(
buildAnnotatedString {
withStyle(style = ParagraphStyle(lineHeight = 30.sp)) {
withStyle(style = SpanStyle(color = Color.Blue)) {
append("Hello\n")
}
withStyle(
style = SpanStyle(
fontWeight = FontWeight.Bold, color = Color.Red
)
) {
append("World\n")
}
append("Compose")
}
}
)
}
includeFontPadding : 텍스트의 첫 번째 줄 상단과 마지막 줄 하단에 글꼴 메트릭에 따라 추가 패딩을 추가하는 레거시 속성
lineHeight : 각 텍스트 줄의 높이를 설정
Text(
text = text,
style = LocalTextStyle.current.merge(
TextStyle(
lineHeight = 2.5.em,
platformStyle = PlatformTextStyle(
includeFontPadding = false
),
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None
)
)
)
)
LineHeightStyle 실험용 API로 텍스트의 가운데 배치를 조정 가능
LineHeightStyle.Trim.None | LineHeightStyle.Trim.Both |
---|---|
![]() |
![]() |
LineHeightStyle.Trim.FirstLineTop | LineHeightStyle.Trim.LastLineBottom |
![]() |
![]() |
Trim API는 includeFontPadding = false에서만 동작할 수 있다
LineBreak : 텍스트를 여러 줄로 분할하는 기준 정의
TextSample(
samples = mapOf(
"Paragraph" to {
Text(
text = SAMPLE_LONG_TEXT,
modifier = Modifier
.width(130.dp)
.border(BorderStroke(1.dp, Color.Gray)),
fontSize = 14.sp,
style = TextStyle.Default.copy(
lineBreak = LineBreak.Paragraph
)
)
}
)
)
TextSample(
samples = mapOf(
"Balanced" to {
val smallScreenAdaptedParagraph =
LineBreak.Paragraph.copy(strategy = LineBreak.Strategy.Balanced)
Text(
text = SAMPLE_LONG_TEXT,
modifier = Modifier
.width(200.dp)
.border(BorderStroke(1.dp, Color.Gray)),
fontSize = 14.sp,
style = TextStyle.Default.copy(
lineBreak = smallScreenAdaptedParagraph
)
)
}
)
)
Strictness/WordBreak API로 설정 가능
Hyphens API로 설정 (None, Auto)
@Composable
fun LongText() {
// 최대 라인
Text("hello ".repeat(50), maxLines = 2)
// 텍스트 오버플로 표시
Text("Hello Compose ".repeat(50), maxLines = 2, overflow = TextOverflow.Ellipsis)
}
사용자 텍스트 입력 구현
@Composable
fun SimpleFilledTextFieldSample() {
var text by remember { mutableStateOf("Hello") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Label") }
)
}
유용한 파라미터
Brush API를 사용하여 고급 스타일을 지정 가능
Brush API 사용은 실험용 상태
var text by remember { mutableStateOf("") }
val brush = remember {
Brush.linearGradient(
colors = rainbowColors
)
}
TextField(
value = text, onValueChange = { text = it }, textStyle = TextStyle(brush = brush)
)
새로 입력할 때마다 TextField 상태가 변경될 때 recomposition에서 brush를 유지하려면 remember를 사용해야 함
지원하는 키보드 옵션 KeyboardOptions
VisualTransformation를 사용하여 시각적 출력을 변경
@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)
)
}
onValueChange에서 전달된 텍스트를 사용하여 변환 로직을 작성 가능
@Composable
fun NoLeadingZeroes() {
var input by rememberSaveable { mutableStateOf("") }
TextField(
value = input,
onValueChange = { newText ->
// 텍스트 앞에 "0" 문자를 제거
input = newText.trimStart { it == '0' }
}
)
}
// SignUpViewModel.kt
class SignUpViewModel(private val userRepository: UserRepository) : ViewModel() {
var username by mutableStateOf("")
private set
fun updateUsername(input: String) {
username = input
}
}
// SignUpScreen.kt
@Composable
fun SignUpScreen(/*...*/) {
OutlinedTextField(
value = viewModel.username,
onValueChange = { username -> viewModel.updateUsername(username) }
/*...*/
)
}
기본적으로 Composable은 선택 가능하지 않다.
SelectionContainer로 래핑 해야 함
@Composable
fun SelectableText() {
SelectionContainer {
Text("This text is selectable")
}
}
특정 영역을 선택 불가능하게 하려면 DisableSelection Composable로 래핑
Text Composable내에서 클릭한 위치를 가져오기 : ClickableText
를 사용
@Composable
fun SimpleClickableText() {
ClickableText(text = AnnotatedString("Click Me"), onClick = { offset ->
Log.d("ClickableText", "$offset -th character is clicked.")
})
}
buildAnnotatedString의 경우
@Composable
fun AnnotatedClickableText() {
val annotatedText = buildAnnotatedString {
append("Click ")
// pop() 호출될 때까지 컨텐츠에 이 URL annotation을 추가
pushStringAnnotation(
tag = "URL", annotation = "https://developer.android.com"
)
withStyle(
style = SpanStyle(
color = Color.Blue, fontWeight = FontWeight.Bold
)
) {
append("here")
}
pop()
}
ClickableText(text = annotatedText, onClick = { offset ->
// 클릭한 위치의 텍스트에 URL annotation이 있는지 확인
annotatedText.getStringAnnotations(
tag = "URL", start = offset, end = offset
).firstOrNull()?.let { annotation ->
Log.d("Clicked URL", annotation.item)
}
})
}
기본으로 제공하는 FontFamily 폰트 사용
@Composable
fun DifferentFonts() {
Column {
Text("Hello World", fontFamily = FontFamily.Serif)
Text("Hello World", fontFamily = FontFamily.SansSerif)
}
}
res/font에 정의된 폰트 사용
val firaSansFamily = FontFamily(
Font(R.font.firasans_regular, FontWeight.Normal),
Font(R.font.firasans_italic, FontWeight.Normal, FontStyle.Italic),
Font(R.font.firasans_bold, FontWeight.Bold),
...
)
Column {
Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Normal)
Text(
text = "text",
fontFamily = firaSansFamily,
fontWeight = FontWeight.Normal,
fontStyle = FontStyle.Italic
)
Text(text = "text", fontFamily = firaSansFamily, fontWeight = FontWeight.Bold)
}
Compose 1.2.0 지원
사용자 정의 공급자가 제공하는 다운로드 가능한 폰트는 현재 미지원
https://developer.android.com/develop/ui/compose/text/fonts
import androidx.compose.ui.text.googlefonts.GoogleFont
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.googlefonts.Font
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 fontName = GoogleFont("Lobster Two")
val fontFamily = FontFamily(
Font(googleFont = fontName, fontProvider = provider)
)
가변 폰트 Android O 이상에서만 지원됩니다.
https://developer.android.com/develop/ui/compose/text/fonts#variable-fonts
// In Typography.kt
@OptIn(ExperimentalTextApi::class)
val displayLargeFontFamily =
FontFamily(
Font(
R.font.robotoflex_variable,
variationSettings = FontVariation.Settings(
FontVariation.weight(950),
FontVariation.width(30f),
FontVariation.slant(-6f),
)
)
)
Compose UI 1.4에서는 API 21까지 이전 Android 버전과의 호환성을 포함하여 최신 그림 이모티콘 버전을 지원
Text, TextField, BasicText, BasicTextField에서 모두 지원함
Emoji2 라이브러리를 추가한 후 EmojiTextView를 사용
<androidx.emoji2.widget.EmojiTextView
android:id="@+id/emoji_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
TextView를 그대로 사용
ComposeView 내에서 AndroidViewBinding를 사용하는 View binding을 통해 구현
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(
ComposeView(this).apply {
setContent {
Column {
Text(EMOJI_TEXT)
AndroidViewBinding(ExampleViewBinding::inflate) {
emojiTextView.text = EMOJI_TEXT
}
}
}
}
)
}
}
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(
ComposeView(this).apply {
setContent {
Column {
Text(EMOJI_TEXT)
AndroidView(
factory = { context -> AppCompatTextView(context) },
update = { it.text = EMOJI_TEXT }
)
}
}
}
)
}
}
comments powered by Disqus
Subscribe to this blog via RSS.
[발표자료] I/O Extended Android in Korea 2024 ~ Whats new in Android development tools
Posted on 21 Jul 2024