How to draw a border around multiline text in compose - android

I want to draw a border around a Text like this,
Text("Box around text",
modifier = Modifier
.padding(top = 8.dp)
.border(width = 2.dp, color = Color.Red)
.background(Color.DarkGray))
Text("Box around text with a very very very very longlonglonglongword",
modifier = Modifier
.padding(top = 8.dp)
.border(width = 2.dp, color = Color.Red)
.background(Color.DarkGray)
)
But in the case of a multiline text, it doesn't look well.
There is a gap on the right between the border and the text.
So how to draw a border around a multiline text, so that it fits the text width?

It looks like a bug, I've created this issue.
Here's how you can restrict it manually:
#Composable
fun WrapTextContent(
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 = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current,
) {
SubcomposeLayout(modifier) { constraints ->
val composable = #Composable { localOnTextLayout: (TextLayoutResult) -> Unit ->
Text(
text = text,
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 = localOnTextLayout,
style = style,
)
}
var textWidthOpt: Int? = null
subcompose("measureView") {
composable { layoutResult ->
textWidthOpt = (0 until layoutResult.lineCount)
.maxOf { line ->
ceil(layoutResult.getLineRight(line) - layoutResult.getLineLeft(line)).toInt()
}
}
}[0].measure(constraints)
val textWidth = textWidthOpt!!
val placeable = subcompose("content") {
composable(onTextLayout)
}[0].measure(constraints.copy(minWidth = textWidth, maxWidth = textWidth))
layout(width = textWidth, height = placeable.height) {
placeable.place(0, 0)
}
}
}
Usage:
var textWidth by remember { mutableStateOf<Int?>(null) }
WrapTextContent(
"Box around text with a very very very very longlonglonglongword",
color = Color.White,
modifier = Modifier
.border(width = 2.dp, color = Color.Red)
.background(Color.DarkGray)
)
Result:

Related

Aligning the text of a TextField with its Trailing icon

I have an svg icon in my drawables folder from our designer, and I'm trying to figure out a way to align the trailing icon of the TextField with its text in a way that it will look the same on most devices. I'm trying to avoid fixed dp values but I can't find a built-in way to do this.
screen code:
var customAmountTxt by remember { mutableStateOf(TextFieldValue()) }
TextField(
value = customAmountTxt,
onValueChange = {
customAmountTxt = it
},
maxLines = 1,
singleLine = true,
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_euro),
contentDescription = stringResource(
R.string.euro_icon_desc
),
modifier = Modifier.padding(
start = 16.dp,
end = 16.dp,
top = 12.dp,
bottom = 12.dp
)
)
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = {
focusManager.clearFocus()
keyboardController?.hide()
}),
shape = RoundedCornerShape(6.dp),
colors = TextFieldDefaults.textFieldColors(
backgroundColor = colorResource(id = R.color.white),
textColor = colorResource(id = R.color.black),
focusedIndicatorColor = colorResource(id = R.color.white),
unfocusedIndicatorColor = colorResource(id = R.color.white),
disabledIndicatorColor = colorResource(id = R.color.white),
cursorColor = colorResource(id = R.color.black)
),
textStyle = TextStyle(
color = Color.Black,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
textAlign = TextAlign.Start
),
modifier = Modifier
.height(50.dp)
.fillMaxWidth()
.shadow(8.dp, shape = RoundedCornerShape(6.dp))
)
current output:

How to align leadingIcon in OutlinedTextField of jetpack compose with the value/placeholder

I am new to jetpack compose, I want my app to load the Exo two light font so I override the material typography like this,
val exoTwoLight = FontFamily(
Font(R.font.exo_two_light),
Font(R.font.exo_two_light, FontWeight.Bold)
)
val exoTwoLightTypography = Typography(
h1 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Bold,
fontSize = 96.sp
),
h2 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Bold,
fontSize = 60.sp
),
h3 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Bold,
fontSize = 48.sp
),
h4 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Bold,
fontSize = 34.sp
),
h5 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
),
h6 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Bold,
fontSize = 12.sp
),
subtitle1 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
subtitle2 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Medium,
fontSize = 14.sp
),
body1 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
),
body2 = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Normal,
fontSize = 14.sp
),
button = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
),
caption = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Normal,
fontSize = 12.sp,
),
overline = TextStyle(
fontFamily = exoTwoLight,
fontWeight = FontWeight.Normal,
fontSize = 10.sp,
)
)
Now when I try to load an input field in the app I can see that placeholder and leading icons are not aligned at all
I tried to customize the placeholder as it is required as a composable
By customization, I mean adding top padding of 2/4 dp to the placeholder
Row(modifier = Modifier.padding(top=10.dp)) {
OutlinedTextField(
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.gallery_icon),
contentDescription = "",
tint = colorResource(R.color.teal_200),
modifier = Modifier.clickable {
}
)
},
placeholder = {
Text(
text = "Placeholder",
style = MaterialTheme.typography.button,
modifier = Modifier.padding(top = 3.dp)
)
},
modifier = Modifier.fillMaxWidth(),
readOnly = false,
value = sampleValue,
onValueChange = {
sampleValue = it
},
maxLines = 1,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
)
}
It only solves the issue for the placeholder but values seem to be misaligned
This is the complete code
var sampleValue by remember {
mutableStateOf("")
}
Column{
Row(modifier = Modifier.padding(top=10.dp)){
OutlinedTextField(
leadingIcon = {
if (true) {
Icon(
painter = painterResource(id = R.drawable.capture_more_images),
contentDescription = "",
tint = colorResource(R.color.teal_200),
modifier = Modifier.clickable {
}
)
} else {
Icon(
painter = painterResource(id = R.drawable.capture_more_images),
contentDescription = "",
tint = colorResource(R.color.disabled)
)
}
},
placeholder = {
Row(
modifier = Modifier.height(24.dp),
verticalAlignment = Alignment.CenterVertically
){
Text(
text = "Placeholder",
style = MaterialTheme.typography.button
)
}
},
modifier = Modifier.fillMaxWidth(),
readOnly = false,
value = sampleValue,
onValueChange = {
sampleValue = it
},
maxLines = 1,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
)
}
Row(modifier = Modifier.padding(top=10.dp)) {
OutlinedTextField(
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.gallery_icon),
contentDescription = "",
tint = colorResource(R.color.teal_200),
modifier = Modifier.clickable {
}
)
},
placeholder = {
Text(
text = "Placeholder",
style = MaterialTheme.typography.button,
modifier = Modifier.padding(top = 3.dp)
)
},
modifier = Modifier.fillMaxWidth(),
readOnly = false,
value = sampleValue,
onValueChange = {
sampleValue = it
},
maxLines = 1,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
)
}
Row(modifier = Modifier.padding(top=10.dp)) {
OutlinedTextField(
placeholder = {
Text(
text = "Placeholder",
style = MaterialTheme.typography.button,
)
},
modifier = Modifier.fillMaxWidth(),
value = sampleValue,
onValueChange = {
sampleValue = it
}
)
}
}
I want that placeholder to be aligned vertically centered ( without giving any top padding ) with the leading icon what is the solution?
The solution to this is to set the singleLine property of the TextField to true. Like this:
TextField(
singleLine = true,
placeholder = {
Text(text = "Search for reminders")
},
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.search_icon),
tint = MaterialTheme.colorScheme.outline,
contentDescription = "Back button"
)
}
)
Many icons have built-in padding on one or more sides. To compensate for this, you can adjust the icon's position using the offset modifier. Set it some positive or negative value:
placeholder = {
Row(
modifier = Modifier.height(24.dp).offset(y = 5.dp),
verticalAlignment = Alignment.CenterVertically
){
Text(
text = "Placeholder",
style = MaterialTheme.typography.button
)
}
}

Jetpack Compose – Why aren't buttonColors taking effect?

I've defined my button composable like this:
#Composable
fun PrimaryButton(modifier: Modifier = Modifier, onClick: Callback, content: #Composable RowScope.() -> Unit) {
val buttonColors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary,
contentColor = contentColorFor(backgroundColor = MaterialTheme.colors.primary)
)
Button(modifier=modifier, onClick = onClick, content=content, colors=buttonColors)
}
I've checked with the debugger and contentColorFor returns the appropiate color (white) (0xFFFFFFFF)
But I can't get to have the text on the button to be white.
Here's the preview
#Preview(name="Buttons")
#Composable
fun PrimaryButtonPreview() {
MyAppTheme {
Row {
PrimaryButton(modifier = Modifier, onClick={}) {
Text(text = "Sample")
}
}
}
}
And the result:
Setting the button = TextStyle(…) results in a change on the text color but I'd like to set it from the button colors rather than having one unique color for the text
Edit
After setting the button's text style color to unspecified it still doesn't work:
Shapes are the default by the Android Studio project generator
Colors
val mainBrown = Color(0xFFB4A996)
val primaryBrown = Color(0xFFC2A686)
val clearBrown = Color(0xFFAE967A)
val white = Color.White
val smokeWhite = Color(0xFFF3F3F3)
val gray = Color(0xFF9AA5AF)
val lightGray =Color(0xFFF1F1EF)
val darkGray = Color(0xFF4D5151)
val black = Color(0xFF4D4646)
val red = Color(248,113,113)
Theme
private val LightColorPalette = darkColors(
primary = primaryBrown,
onPrimary = white,
primaryVariant = primaryBrown,
secondary = white,
onSecondary = primaryBrown,
secondaryVariant = white,
background = darkGray,
surface = lightGray,
onSurface = black,
error = red,
onError = smokeWhite,
)
#Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: #Composable () -> Unit
) {
MaterialTheme(
colors = LightColorPalette,
typography = typography,
shapes = Shapes,
content = content
)
}
Typography
val typography = Typography(
h1 = TextStyle(
color = mainBrown,
fontWeight = FontWeight.W700,
fontSize = 1.5f.rem,
lineHeight = 2.rem,
),
h2 = TextStyle(
color = mainBrown,
fontWeight = FontWeight.W500,
fontSize = 1.25f.rem,
lineHeight = 1.75f.rem
),
h3 = TextStyle(
color = mainBrown,
fontWeight = FontWeight.Normal,
fontSize = 1.5f.rem,
lineHeight = 2.rem
),
h4 = TextStyle(
color = mainBrown,
fontWeight = FontWeight.Normal,
fontSize = 1.25f.rem,
lineHeight = 1.75f.rem
),
body1 = TextStyle(
color = black,
fontSize = 1.rem,
lineHeight = 1.25f.rem
),
button = TextStyle(
fontSize = 1.rem,
color = Color.Unspecified,
lineHeight = 1.25f.rem
),
defaultFontFamily = ralewayRegular
)
My setup was fine except for the typography.
I was setting the color every time so that takes over how it should look from the MaterialTheme.
Don't set those values and let the framework handle it for you.
Not working because I am setting (and forcing) mainBrown as the color
val typography = Typography(
h1 = TextStyle(
color = mainBrown,
fontWeight = FontWeight.W700,
fontSize = 1.5f.rem,
lineHeight = 2.rem,
),
h2 = TextStyle(
color = mainBrown,
fontWeight = FontWeight.W500,
fontSize = 1.25f.rem,
lineHeight = 1.75f.rem
),
h3 = TextStyle(
color = mainBrown,
fontWeight = FontWeight.Normal,
fontSize = 1.5f.rem,
lineHeight = 2.rem
),
h4 = TextStyle(
color = mainBrown,
fontWeight = FontWeight.Normal,
fontSize = 1.25f.rem,
lineHeight = 1.75f.rem
),
body1 = TextStyle(
color = black,
fontSize = 1.rem,
lineHeight = 1.25f.rem
),
button = TextStyle(
fontSize = 1.rem,
color = Color.Unspecified,
lineHeight = 1.25f.rem
),
defaultFontFamily = ralewayRegular
)
Working
val typography = Typography(
h1 = TextStyle(
fontWeight = FontWeight.W700,
fontSize = 1.5f.rem,
lineHeight = 2.rem,
),
h2 = TextStyle(
fontWeight = FontWeight.W500,
fontSize = 1.25f.rem,
lineHeight = 1.75f.rem
),
h3 = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 1.5f.rem,
lineHeight = 2.rem
),
h4 = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 1.25f.rem,
lineHeight = 1.75f.rem
),
body1 = TextStyle(
fontSize = 1.rem,
lineHeight = 1.25f.rem
),
button = TextStyle(
fontSize = 1.rem,
color = Color.Unspecified,
lineHeight = 1.25f.rem
),
defaultFontFamily = ralewayRegular
)

How contentColor is applied to child in surface in jetpack compose

How contentColor affects the childs of Surface internally in Jetpack compose for example in this example the text color will be cyan.
Let's look at the Surface code:
#Composable
private fun Surface(
modifier: Modifier,
shape: Shape,
color: Color,
contentColor: Color,
border: BorderStroke?,
elevation: Dp,
clickAndSemanticsModifier: Modifier,
content: #Composable () -> Unit
) {
val elevationOverlay = LocalElevationOverlay.current
val absoluteElevation = LocalAbsoluteElevation.current + elevation
val backgroundColor = if (color == MaterialTheme.colors.surface && elevationOverlay != null) {
elevationOverlay.apply(color, absoluteElevation)
} else {
color
}
CompositionLocalProvider(
LocalContentColor provides contentColor,
LocalAbsoluteElevation provides absoluteElevation
) {
Box(
modifier
.shadow(elevation, shape, clip = false)
.then(if (border != null) Modifier.border(border, shape) else Modifier)
.background(
color = backgroundColor,
shape = shape
)
.clip(shape)
.then(clickAndSemanticsModifier),
propagateMinConstraints = true
) {
content()
}
}
}
You can see here that Surface is setting up the CompositionLocalProvider for value LocalContentColor. A lot of Material composables are using LocalContentColor as a default value of their color, in your example Text:
#Composable
fun Text(
text: AnnotatedString,
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 = Int.MAX_VALUE,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
}
val mergedStyle = style.merge(
TextStyle(
color = textColor,
fontSize = fontSize,
fontWeight = fontWeight,
textAlign = textAlign,
lineHeight = lineHeight,
fontFamily = fontFamily,
textDecoration = textDecoration,
fontStyle = fontStyle,
letterSpacing = letterSpacing
)
)
BasicText(
text,
modifier,
mergedStyle,
onTextLayout,
overflow,
softWrap,
maxLines,
inlineContent
)
}
And especially this part:
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
}
Here you can see that Text is trying to get color defined as a parameter. If it's Unspecified it tries to get one from the TextStyle. If there is no color in there - Text is using value from LocalContentColor.current (mixed with LocalContentAlpha, but that's topic for another question).

ColorTrackText in compose

I want to write this type of text ,is this possible?
Using Modifier.drawWithContent and clipRect you can draw only part of the contents of the view. Then you need to put two identical Text in a Box and draw the desired part of each view:
#Composable
fun TwoColorText(
text: String,
color1: Color,
color2: Color,
part: Float,
modifier: Modifier = Modifier,
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 = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
val textView = #Composable { color: Color, leftMultiplier: Float, rightMultiplier: Float ->
Text(
text,
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,
modifier = Modifier.drawWithContent {
clipRect(
left = size.width * leftMultiplier,
right = size.width * rightMultiplier
) {
this#drawWithContent.drawContent()
}
}
)
}
Box(modifier) {
textView(color1, 0f, part)
textView(color2, part, 1f)
}
}
Usage:
Row(Modifier.padding(10.dp)) {
TwoColorText(
"新鲜",
color1 = Color.Black,
color2 = Color.Red,
part = 0.27f,
)
Spacer(modifier = Modifier.width(50.dp))
TwoColorText(
"消息",
color1 = Color.Black,
color2 = Color.Red,
part = 0.27f,
)
}
Result:

Categories

Resources