Welcome to the third part of Carthographing Jetpack Compose. In the previous episode we took a look at images and saw how to draw using Canvas()
. Today we focus on text. As you will see, text-related features are present in quite a few packages. So, the aim of this post is to give you an understanding of what to find where.
Easy to use
Showing text in Jetpack Compose is really easy. The following code snippet will give you a centered italic text. Its size is provided as scale-independent pixels (sp
).
Text(
modifier = Modifier.fillMaxWidth(),
text = "Hello Compose",
textAlign = TextAlign.Center,
fontStyle = FontStyle.Italic,
fontSize = 64.sp
)
Text()
belongs to androidx.compose.material
. If you don't need the Material goodness, there is a nice BasicText()
composable in androidx.compose.foundation.text
.
BasicText(
text = "Hello Compose"
)
Have you noticed that I did not pass textAlign
, fontStyle
or fontSize
? Well, you can't. As parameters, that is. To configure the appearance of your text you use annotated strings. Take a look:
val l = mutableListOf<AnnotatedString.Range<SpanStyle>>()
l.add(AnnotatedString.Range(SpanStyle(Color.Red), 0, 5))
l.add(AnnotatedString.Range(SpanStyle(Color.Blue), 6, 13))
BasicText(
text = AnnotatedString(
text = "Hello Compose",
spanStyles = l
)
)
The immutable class AnnotatedString
belongs to androidx.compose.ui.text
. The docs say:
The basic data structure of text with multiple styles. To
construct anAnnotatedString
you can useBuilder
.
The parameter spanStyles
is
a list of
Range
s that specifiesSpanStyle
s on certain
portion of the text. These styles will be applied in the order
of the list. And theSpanStyle
s applied later can override the
former styles.
My example defines two distinct Range
s inside the annotated string. Ranges contain an item of some type (for example SpanStyle
), a start (when the range takes effect), and an end. Please note that end refers to the position where the item is no longer in effect. My ranges differ in color. SpanStyle
can be configured extensively, for example using fontSize
, fontStyle
, or background
.
Now let's see how the builder the docs mentioned works:
BasicText(
text = buildAnnotatedString {
withStyle(
style = SpanStyle(
fontSize = 64.sp,
color = Color.Yellow,
background = Color.LightGray
)
) {
append("Hello Compose")
}
addStyle(
style = ParagraphStyle(textAlign = TextAlign.Center),
start = 0, end = length
)
}
)
buildAnnotatedString()
belongs to androidx.compose.ui.text
, just like AnnotatedString
. In fact they currently share the same source file, AnnotatedString.kt. The class AnnotatedString.Builder
allows the construction of an AnnotatedString
using methods such as withStyle()
, append()
, and addStyle()
. My example sets the basic appearance using withStyle()
and adds th text with append()
. addStyle()
centers my text. Have you noticed end = length
? length
saves you from calculating the length of the text on your own.
Clickable text
You can make a text clickable the same way as you would with any other composable.
BasicText(
text = "Hello Compose",
modifier = Modifier.clickable {
println("Hello")
}
)
This is super convenient for things like Click to start. But what if you need to know which part of the text the user has clicked?
ClickableText(
text = AnnotatedString(text = "Click here"),
onClick = {
println(it)
}
)
The composable ClickableText()
belongs to androidx.compose.foundation.text
. The docs say:
A continent version of
BasicText
component to be able to
handle click event on the text.This is a shorthand of
BasicText
withpointerInput
to be
able to handle click event easily.
onClick
is executed when users click the text. The callback receives clicked character's offset.
Other text-related functions
So far I have shown you text-related functions inside Jetpack Compose. But there is another Jetpack (not compose) package dealing with text: androidx.core.text
. It contains some nice extension functions, e.g. htmlEncode()
. Let's see how it works.
So the extension function passes its parameter to TextUtils.htmlEncode()
. The class TextUtils
belongs to package android.text
. We thus jump into the Android framework. It is worth mentioning that androidx.core.text
contains a class called TextUtilsCompat
which also features a static htmlEncode()
function. If Jetpack Compose used this, it might eliminate one dependency to the platform, which could help porting to Compose for Desktop. I'll follow up on this shortly.
But what does htmlEncode()
do? The Android docs say:
Html-encode the string.
This leaves room for explanations. 😎 Let's peek into the androidx.core.text.TextUtilsCompat
version.
So we learn two things:
-
htmlEncode()
makes certain characters html-friendly - On newer platform versions even
TextUtilsCompat
invokes the platform so my assumption stated above does not hold. To do so the code would need to be altered to always doing theelse
part.
Before finishing this part I would like to mention that extension function capitalize()
is available in both kotlin.text
(yet another text-related package) and androidx.compose.ui.text
. The first one is deprecated.
The second one is not. So in your Compose apps you might consider using androidx.compose.ui.text.capitalize()
which receives a androidx.compose.ui.text.intl.Locale
.
Conclusion
Text support in Jetpack Compose is distributed over quite a few packages and sub packages. Regarding your Compose apps the most important decision will be if you use the Material version or the more basic ones. Did I miss something important? Kindly share your thoughts in the comments.
Top comments (0)