Reputation: 1943
I have already built most of the component below but I am stuck on a detail. The detail is that the whole content should be vertically centered and additionally the Icon
component should be centrally aligned to the first line of the Text
component.(Below is the desired output)
@Composable
private fun MyBaseComponent(
@DrawableRes iconResId: Int? = null,
title: String? = null,
subtitle: String,
backgroundColor: Color,
itemColor: Color
) {
Box(
modifier = Modifier
.heightIn(min = 48.dp)
.fillMaxWidth()
.background(color = backgroundColor, shape = RoundedCornerShape(2.dp))
) {
Row(
modifier = Modifier.padding(MyTheme.spacing.double),
verticalAlignment = Alignment.CenterVertically
) {
iconResId?.let {
MyIcon(iconResId = iconResId, tint = itemColor)
Spacer(modifier = Modifier.padding(end = MyTheme.spacing.double))
}
Column(verticalArrangement = Arrangement.spacedBy(MyTheme.spacing.small)) {
title?.let { MyTitle(text = title, color = itemColor) }
MySubtitle(text = subtitle, color = itemColor)
}
}
}
}
How it looks like at the moment. Everything seems to be alright but except the icon positioning.
Upvotes: 11
Views: 7689
Reputation: 589
A simple way is to use the parent composable padding to align the top of the icon and the text:
@Preview
@Composable
fun IconText(
) {
val title =
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed do."
val subtitle =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
Box(
modifier = Modifier
.heightIn(min = 48.dp)
.background(color = Color.White, shape = RoundedCornerShape(2.dp))
) {
Box(modifier = Modifier.padding(top = 40.dp, bottom = 40.dp)) {
Image(
painter = painterResource(id = R.drawable.ic_check),
colorFilter = ColorFilter.tint(Color.Gray),
contentDescription = "",
modifier = Modifier.padding(start = 10.dp)
)
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier.padding(start = 40.dp, end = 40.dp)
) {
Text(
text = title,
color = Color.Black,
fontSize = 15.sp,
fontWeight = FontWeight.Bold
)
Text(text = subtitle, color = Color.Black)
}
}
}
}
Result:
A more complicated way is to use 'onTextLayout' to find the location of the line:
@Composable
fun TextLineIcon(
text: String,
icon: ImageVector,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontWeight: FontWeight? = null,
iconRightPadding: Dp = 0.dp,
iconLine: Int = 0,
iconTint: Color = Color.Black
// etc
) {
val painter = rememberVectorPainter(image = icon)
var lineTop = 0f
var lineBottom = 0f
var lineLeft = 0f
with(LocalDensity.current) {
val imageSize = Size(icon.defaultWidth.toPx(), icon.defaultHeight.toPx())
val rightPadding = iconRightPadding.toPx()
Text(
text = text,
color = color,
fontSize = fontSize,
fontWeight = fontWeight,
onTextLayout = { layoutResult ->
val nbLines = layoutResult.lineCount
if (nbLines > iconLine) {
lineTop = layoutResult.getLineTop(iconLine)
lineBottom = layoutResult.getLineBottom(iconLine)
lineLeft = layoutResult.getLineLeft(iconLine)
}
},
modifier = modifier.drawBehind {
with(painter) {
translate(
left = lineLeft - imageSize.width - rightPadding,
top = lineTop + (lineBottom - lineTop) / 2 - imageSize.height / 2,
) {
draw(painter.intrinsicSize, colorFilter = ColorFilter.tint(iconTint))
}
}
}
)
}
}
Usage:
@Preview
@Composable
fun IconText2(
) {
val title =
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed do."
val subtitle =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
Box(
modifier = Modifier
.heightIn(min = 48.dp)
.background(color = Color.White, shape = RoundedCornerShape(2.dp))
) {
Box {
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier.padding(40.dp)
) {
TextLineIcon(
title,
Icons.Filled.Check,
fontWeight = FontWeight.Bold,
fontSize = 15.sp,
iconRightPadding = 4.dp,
iconTint = Color.Gray
)
Text(text = subtitle, color = Color.Black)
TextLineIcon(
title,
Icons.Filled.PlusOne,
fontWeight = FontWeight.Bold,
fontSize = 15.sp,
iconRightPadding = 4.dp,
iconLine = 1,
iconTint = Color.Red
)
Text(text = subtitle, color = Color.Black)
}
}
}
}
You can even chose the line you want the icon to be aligned with.
Upvotes: 10
Reputation: 1030
verticalAlignment = Alignment.CenterVertically
it will align every item inside the row
vertically.
for example: your row
is a size of 50.dp
and column
which contains the two textView
are almost equal to 45.dp
, alright? now that will aligned centerVertically that's fine but the problem is your icon
which is size of for instance 24.dp
if we look at the ratio of 50.dp(row)
, 45.dp(column)
then icon
which is 24.dp
pretty big according to 50.dp
, we can't clearly see behaviour of 45.dp
according to 50.dp
but according to 24.dp
it can clearly be seen.
You have to remove verticalAlignment = Alignment.CenterVertically
from the row.
Sample:
@Composable
fun AlignmentProblem(
modifier: Modifier = Modifier
) {
Box(modifier = modifier) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
.background(Color.Cyan)
) {
Icon(
painter = painterResource(id = R.drawable.ic_chat),
contentDescription = null
)
Column(
modifier = Modifier.fillMaxWidth()
.background(Color.LightGray),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "What is Lorem Ipsum?")
Text(text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.")
}
}
}
}
Upvotes: -1
Reputation: 7602
Either use padding(top = x.dp)
to offset the icon or create a different hierarchy, something like:
Column() {
MyTitle() // Also align to end of composable
Row {
Icon()
MySubtitle() // Align to end
}
}
Might need some adjustmends but hopefully you get the idea
Upvotes: -1