I am trying to change the style of a clickable text, mostly the font and color of it.
I am using for now:
text = AnnotatedString(stringResource(R.string.forgot_password)),
onClick = { offset ->
Log.d("ClickableText", "$offset -th character is clicked.")
} ```
This just using the default theme.
How can I apply a different color or font or fontsize ?
Thanks
It offers the style parameter. You could just do something like
ClickableText(
text = AnnotatedString(""),
onClick = {},
style = TextStyle(
color = Blue,
fontSize = 26.sp,
fontFamily = FontFamily.Cursive
)
)
If you are using Android Studio, you can just press Ctrl + P on Windows and Cmd + P on Mac to check the available parameters. The optional parameters are not inserted by code completion since they can be many.
To use fonts, define a fontFamily property, like this
private val Montserrat = FontFamily(
Font(R.font.montserrat_regular, FontWeight.Normal),
Font(R.font.montserrat_medium, FontWeight.Medium),
Font(R.font.montserrat_bold, FontWeight.Bold),
)
then add it to your Typography
val Typography = Typography(
h1 = TextStyle(
fontFamily = Montserrat,
fontSize = 96.sp,
fontWeight = FontWeight.Normal,
lineHeight = 117.sp,
letterSpacing = (-1.5).sp
),
which is added to your theme
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
If you then use these styles in your text it will pick the family you specified, or you can override, like this
Text(
text = "Sample text",
style = MaterialTheme.typography.body1.copy(
color = Color.Blue,
fontFamily = Montserrat,
),
)
You can use style keyword to add font, for example:
ClickableText(
text = AnnotatedString(text = "Clickable Text Font Example"),
style = TextStyle(
fontFamily = FontFamily(Font(R.font.your_font)),
),
onClick = {}
)
Related
I am working with MediumTopAppBar, and i am trying to change the textStyle of the big Title without ruining the small title.
So what i am doing is:
MediumTopAppBar(
title = { HeadlineLargeBlackText(text = "pageTitle") },
navigationIcon = {
IconButton(onClick = { onBackPressed.invoke() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(id = R.string.action_back)
)
}
},
scrollBehavior = scrollBehavior
)
My problem is that when i set it to "HeadlineLargeBlackText" it automatically change the textstyle of both the big and the small text so it looks like this (image taken with both big and small text visible):
What i want is for the bottom to be big, and the top to be small.
When i go into the MediumTopAppBar i do see that the textStyle of the big and the small is set in the TwoRowsTopAppBar as:
titleTextStyle = MaterialTheme.typography.fromToken(TopAppBarMediumTokens.HeadlineFont),
smallTitleTextStyle = MaterialTheme.typography.fromToken(TopAppBarSmallTokens.HeadlineFont),
But ofc that method is private :// So i wonder, is it possible to get access to these big/small texts separately somehow?
Currently the MediumTopAppBar uses the titleLarge and headlineSmall textstyles defined with the typography attribute in your theme.
You can define them in the theme with:
val customTypography = androidx.compose.material3.Typography(
titleLarge = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 22. sp,
lineHeight = 28. sp,
letterSpacing = 0. sp
),
headlineSmall = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 16. sp,
lineHeight = 24. sp,
letterSpacing = 0.15.sp
),
)
If you don't wont to apply them to the whole app, you can apply a custom theme only to the MediumTopAppBar with something like:
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
AppTheme {
MediumTopAppBar(
title = {
androidx.compose.material3.Text(
"Medium TopAppBar",
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
//....
)
},
//....
}
where
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: #Composable() () -> Unit
) {
//...
aMaterialTheme(
colorScheme = colors,
typography = customTypography,
content = content
)
}
Previously you could define all of your style changes as a single style in XML which was really convenient especially if you had a lot of different styles. The project I'm currently working on has 50+ of these styles defined.
<!-- styles.xml -->
<style name="title_1">
<item name="android:textColor">#color/purple_500</item>
<item name="android:fontFamily">#font</item>
<item name="android:textSize">18sp</item>
<item name="android:maxLines">1</item>
<item name="android:firstBaselineToTopHeight">12sp</item>
<item name="android:lastBaselineToBottomHeight">9sp</item>
</style>
<!-- hello_world.xml -->
<TextView
android:id="#+id/hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, World!"
style="#style/title_1" />
While looking to transition to Compose UI, I noticed that certain attributes are separated out. For example baseline can be changed as a modifier while other values can be changed as a text style or a separate parameter in the case of max lines.
// Styles.kt
#Composable
fun Title1(text: String) {
Text(
text = text,
modifier = Modifier.paddingFromBaseline(top = 12.sp, bottom = 9.sp),
maxLines = 1,
style = TextStyle(
color = colorResource(id = R.color.purple_500),
fontFamily = FontFamily(Font(R.font.podkova_semibold)),
fontSize = 18.sp
)
)
}
// MainActivity.kt
setContent {
Title1("Hello, World")
}
This makes it more error prone since they're separated into different parameters and it's easy to forget to apply one of them. I know you could lump all the text styles together, which helps, but was wondering if there was a way to replicate what we had in XML and specify all the modifications in one place.
In my initial approach above I tried creating wrappers around the Text composable with all the predefined changes, but wanted a flexible solution that can take all the Text parameters without having 50+ composables with all the same parameters as the Text composable. Wondering if there's something in the Kotlin language that can help simplify this or if this is just the wrong approach to the problem.
// Would like to avoid having to do this 50+ times
#Composable
fun Title1(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = 1,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
Text(
text = text,
modifier = modifier
.paddingFromBaseline(top = 12.sp, bottom = 9.sp),
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
onTextLayout = onTextLayout,
style = style.merge(
TextStyle(
color = colorResource(id = R.color.purple_500),
fontFamily = FontFamily(Font(R.font.podkova_semibold)),
fontSize = 18.sp
)
)
)
}
I think your initial approach is correct in the current version of Jetpack Compose (1.1.1), but they're currently working on APIs to enable setting this within a Text's TextStyle, so you can keep it all in one place, similar to XML.
See Compose UI 1.2.0-beta01: https://developer.android.com/jetpack/androidx/releases/compose-ui#version_12_2
It looks like they'll be adding a lineHeightStyle property to TextStyle, in addition to platformStyle (which allows setting includeFontPadding to false).
You Can create it by making Typography.
object MyStyle{
val myFontFamily = FontFamily(
Font(R.font.custom_font, FontWeight.Bold)
)
val myTypepo = Typography(
bodyLarge = TextStyle(
fontFamily = myFontFamily,
fontWeight = FontWeight.Bold,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
),)
}
Now OnTextView you Can use it like :
Text(text = title, style = MyStyle.myTypepo.bodyLarge)
You can use this code, when you apply your theme and it will be implemented all across the application
MaterialTheme(
colorScheme = MyColorScheme,
typography = MyTypo,
content = {
ProvideTextStyle(value = defaultTextStyle, content)
}
)
I'm trying to make a search bar using BasicTextField.
The default text color is black and I couldn't find any option to change the text color.
Also I need to change the text size, font and other attributes.
#Composable
fun MyBasicTextField(){
var text by remember { mutableStateOf("Hello") }
BasicTextField(
value = text,
onValueChange = {
text = it
},
decorationBox = { ... },
modifier = Modifier
.fillMaxWidth()
.background(Color.DarkGray, CircleShape)
.padding(10.dp)
)
}
If this is not possible through BasicTextField, is there any other approach to create similar view?
I have tried TextField but there was several problems like removing label, height, background...
You need textStyle parameter. If you prefer using default text style, use LocalTextStyle:
BasicTextField(
// ...
textStyle = LocalTextStyle.current.copy(color = Color.White)
)
Or you can use one of material styles:
BasicTextField(
// ...
textStyle = MaterialTheme.typography.body1.copy(color = Color.White)
)
In addition to other answer, this worked for me in adding textStyle property:
textStyle = TextStyle(
color = Color.White,
fontFamily = FontFamily.SansSerif,
fontSize = 14.sp,
textAlign = TextAlign.Center,
)
here is all the TextStyle attributes that you can set :
https://developer.android.com/reference/kotlin/androidx/compose/ui/text/TextStyle
public constructor TextStyle(
color: Color,
fontSize: TextUnit,
fontWeight: FontWeight?,
fontStyle: FontStyle?,
fontSynthesis: FontSynthesis?,
fontFamily: FontFamily?,
fontFeatureSettings: String?,
letterSpacing: TextUnit,
baselineShift: BaselineShift?,
textGeometricTransform: TextGeometricTransform?,
localeList: LocaleList?,
background: Color,
textDecoration: TextDecoration?,
shadow: Shadow?,
textAlign: TextAlign?,
textDirection: TextDirection?,
lineHeight: TextUnit,
textIndent: TextIndent?
)
If you want to use your current Material theme's colors so that it adapts to light and dark themes, you need to use LocalContentColor.current as well.
val localStyle = LocalTextStyle.current
val mergedStyle = localStyle.merge(TextStyle(color = LocalContentColor.current))
BasicTextField(
textStyle = mergedStyle,
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
)
You probably also want to change the color of your cursor brush from the default black one. After some digging, I found that TextField uses the primary color (at least for Material 3).
In Jetpack Compose, when using Text, we can center text using TextAlign:
Text(
text = "How many cars are in the garage",
textAlign = TextAlign.Center
)
But if we're looking to get the position of a click within a Text composable, we use ClickableText:
ClickableText(
text = AnnotatedString("Click Me"),
onClick = { offset ->
Log.d("ClickableText", "$offset -th character is clicked.")
}
)
However I don't see how to center text in ClickableText? I can use gravity, but that will only center the component, not the text inside of it.
You can define the textAlign in the style parameter:
ClickableText(
text = AnnotatedString("Click Me"),
style = TextStyle(
textAlign = TextAlign.Center),
onClick = { offset ->
}
)
In Jetpack Compose, you can set your specific font by editing the type file:
val MyFont= FontFamily(
Font(R.font.myfont, FontWeight.Normal)
)
// Set of Material typography styles to start with
val Typography = Typography(
body1 = TextStyle(
fontFamily = MyFont,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
button = TextStyle(
fontFamily = MyFont,
fontWeight = FontWeight.SemiBold,
fontSize = 14.sp
),
defaultFontFamily = MyFont
)
But my problem is that I have multiple locales, and I want to set the specific font for each one.
Before using Compose, my approach was to create a style.xml file in each language values folder and edit the style.xml in a way that changes the font family. but this approach won't work when using Compose.
So how can I have a different font family for each locale?
If you want to achieve this you need to follow some steps as below:
These fonts should be the same name and put in the different font locale folder.
Define FontFamily with that font name
val myFontFamily = FontFamily(
Font(R.font.lato_light, FontWeight.Light),
Font(R.font.lato_regular, FontWeight.Normal),
Font(R.font.lato_italic, FontWeight.Normal, FontStyle.Italic),
Font(R.font.lato_regular, FontWeight.Medium),
Font(R.font.lato_bold, FontWeight.Bold)
)
Use in your Text
val text = "Hello Android Developers"
Column(Modifier.fillMaxWidth().padding(16.dp)) {
Text(text, fontFamily = myFontFamily, fontWeight = FontWeight.Light)
Text(text, fontFamily = myFontFamily, fontWeight = FontWeight.Medium)
Text(text, fontFamily = myFontFamily, fontStyle = FontStyle.Italic)
}
The result of English and Spanish locale as below:
Not sure if there is any better approach, but I think can implement it using simple Strategy Pattern.
Create an interface for the fontFamily, i.e.
interface MyFont {
fun getBody1FontFamily()
fun getButtonFontFamily()
}
and then implement it i.e.
class EnglishFont: MyFont {
fun getBody1FontFamily() = // font for body1 on English locale
fun getButtonFontFamily() = // font for button on English locale
}
class KoreanFont: MyFont {
fun getBody1FontFamily() = // font for body1 on Korean locale
fun getButtonFontFamily() = // font for button on Korean locale
}
Then, in your composable function you can create your myFont object based on the locale and inject the object to Typography
val Typography = Typography(
body1 = TextStyle(
fontFamily = myFont.getBody1FontFamily(),
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
button = TextStyle(
fontFamily = myFont.getButtonFontFamily(),
fontWeight = FontWeight.SemiBold,
fontSize = 14.sp
),
defaultFontFamily = MyFont
)