androidandroid-jetpack-composeandroid-jetpack-compose-layoutandroid-compose-layout

TextField overflow and softwrap not working with Compose Constraint Layout


I've been trying to build a list with a Card in it formatted like this:

Card Content

The difficulty here is that the title e.g. "Bread" and ingredient name e.g. "Flour" can be very long and thus I want to have an ellipsis to keep things manageable i.e. "My Long Flour name" will be displayed as "My Long Flou..." or as much space as is allowed. The picture size and the gram and percent widths are constant .dp values.

Ellipsis worked fine when it was in a Column but with ConstraintLayout it doesn't seem to work and I get this: enter image description here

here's my code

@Composable
fun BakeItem(
    modifier: Modifier = Modifier,
    bake: Bake,
    cardClicked: () -> Unit,
    ingredeints: List<Ingredient>
) {
    Card(
        modifier = modifier
            .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp)
            .clickable { cardClicked() }
            .fillMaxSize()
            .wrapContentHeight(),
        border = BorderStroke(4.dp, MaterialTheme.colorScheme.secondary),
        shape = RoundedCornerShape(14.0.dp),
        colors = cardColors(
            containerColor = MaterialTheme.colorScheme.background
        )
    ) {
        val context = LocalContext.current
        val uri = remember(bake.imagePath) { Uri.parse(bake.imagePath) }
        // Card Content

        ConstraintLayout(
            modifier = modifier
                .fillMaxSize()
                .padding(start = 16.dp, top = 8.dp, end = 8.dp, bottom = 8.dp)
        ) {
            val (titleRef, gramColRef, ingrColRef, percentColRef,
                imageRef, dateRef, starsRef) = createRefs()

            Text(
                modifier = modifier
                    .padding(4.dp)
                    .constrainAs(titleRef) {
                        top.linkTo(parent.top, margin = 8.dp)
//                        end.linkTo(imageRef.start, margin = 8.dp)
                        start.linkTo(parent.start, margin = 8.dp)
                    }
                    .background(Color(0xffeeeeee)),
//                textAlign = TextAlign.Left,
                text = if (bake.recipeName.isEmpty()) "<Unnamed>" else bake.recipeName,
                style = MaterialTheme.typography.titleLarge,
                fontWeight = FontWeight.Bold,
                color = MaterialTheme.colorScheme.primary,
                softWrap = false,
                overflow = TextOverflow.Ellipsis,

                )

            Column(
                horizontalAlignment = Alignment.End,
                modifier = modifier
                    .width(50.dp)
                    .constrainAs(gramColRef) {
                        top.linkTo(titleRef.bottom, margin = 8.dp)
                        end.linkTo(ingrColRef.start, margin = 8.dp)
                        start.linkTo(parent.start, margin = 8.dp)
                    }
            ) {
                ingredeints.forEachIndexed { _, it ->
                    Text(
                        text = it.weightGram,
                        style = MaterialTheme.typography.titleSmall,
                        softWrap = false,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }
            Column(
                modifier = modifier
                    .constrainAs(ingrColRef) {
                        top.linkTo(titleRef.bottom, margin = 8.dp)
                        start.linkTo(gramColRef.end, margin = 8.dp)
                        end.linkTo(ingrColRef.start, margin = 8.dp)
                    },

                ) {
                ingredeints.forEachIndexed { _, it ->
                    Text(
                        text = it.name,
                        style = MaterialTheme.typography.titleSmall,
                        softWrap = false,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }
            Column(
                modifier = modifier
                    .width(50.dp)
                    .constrainAs(percentColRef) {
                        top.linkTo(titleRef.bottom, margin = 8.dp)
                        end.linkTo(imageRef.start, margin = 8.dp)
                        start.linkTo(ingrColRef.end, margin = 8.dp)
                    },
                horizontalAlignment = Alignment.End
            ) {
                ingredeints.forEachIndexed { i, it ->
                    Text(
                        text = if (i == 0) "" else it.bakingPercent,
                        style = MaterialTheme.typography.titleSmall,
                        softWrap = false,
                        overflow = TextOverflow.Ellipsis
                    )
                }
            }

            Text(
                modifier = modifier.padding(
                    top = 8.dp,
                    start = 4.dp,
                    end = 4.dp,
                    bottom = 4.dp
                ),
                text = bake.notes,
                style = MaterialTheme.typography.titleSmall,
                color = MaterialTheme.colorScheme.onSurface,
                maxLines = 3
            )

            if (bake.imagePath.isNotEmpty()) {
                Image(
                    modifier = modifier
                        .constrainAs(imageRef) {
                            top.linkTo(parent.top, margin = 8.dp)
                            end.linkTo(parent.end, margin = 8.dp)
                        }
                        .padding(4.dp)
                        .requiredSize(150.dp)
                        .clip(RoundedCornerShape(14.dp))
                        .border(
                            4.dp,
                            MaterialTheme.colorScheme.primaryContainer,
                            RoundedCornerShape(14.dp)
                        ),
                    painter = rememberAsyncImagePainter(
                        remember(uri) {
                            ImageRequest.Builder(context)
                                .data(uri)
                                // TODO, think of caching improvements
                                //              .diskCacheKey(uri.toString() + key.value)
                                //              .memoryCacheKey(uri.toString() + key.value)
                                .diskCachePolicy(CachePolicy.DISABLED)
                                .memoryCachePolicy(CachePolicy.DISABLED)
                                .build()
                        }
                    ),
                    contentScale = ContentScale.Crop,
                    contentDescription = "Image of your bake"
                )
            } else {
                Spacer(modifier = modifier
                    .background(Color.Blue)
                    .width(150.dp)
                    .height(10.dp)
                    .constrainAs(imageRef) {
                        top.linkTo(parent.top, margin = 8.dp)
                        end.linkTo(parent.end, margin = 8.dp)
                    })
            }

            Text(
                modifier = modifier
                    .padding(4.dp)
                    .constrainAs(dateRef) {
                        bottom.linkTo(parent.bottom, margin = 8.dp)
                        top.linkTo(imageRef.bottom, margin = 8.dp)
                        end.linkTo(parent.end, margin = 8.dp)
                    },
                text = bake.startTime.toString(),
                style = MaterialTheme.typography.titleMedium,
                color = MaterialTheme.colorScheme.onSurface,
//                    textAlign = Layout.Alignment.ALIGN_CENTER
            )

            createHorizontalChain(
                gramColRef, ingrColRef, percentColRef, imageRef,
                chainStyle = ChainStyle.SpreadInside
            )
            createVerticalChain(
                imageRef, dateRef,
                chainStyle = ChainStyle.SpreadInside
            )
            createHorizontalChain(
                titleRef, imageRef,
                chainStyle = ChainStyle.SpreadInside
            )
        }
    }
}

Solution

  • In the title you have to add as constrain width = Dimension.fillToConstraints, end.linkTo(imageRef.start, margin = 8.dp) and maxLines = 1:

    Text(
        modifier = Modifier
            .padding(4.dp)
            .constrainAs(titleRef) {
                top.linkTo(parent.top, margin = 8.dp)
                end.linkTo(imageRef.start, margin = 8.dp)
                start.linkTo(parent.start, margin = 8.dp)
                width = Dimension.fillToConstraints
            }
            .background(Color(0xffeeeeee)),
        text = "Recipe Name",
        //...
        maxLines = 1,
        overflow = TextOverflow.Ellipsis,
    )
    

    For the list instead of using 3 Columns you do can something different.
    For the ingredient name add the weight(1f) modifier and the maxLines = 1

            Column(
                modifier = Modifier
                    .constrainAs(listRef) {
                        top.linkTo(titleRef.bottom, margin = 8.dp)
                        end.linkTo(imageRef.start, margin = 8.dp)
                        start.linkTo(parent.start, margin = 8.dp)
                        width = Dimension.fillToConstraints
                    }
            ) {
                //forEach Row...
    
                Row(Modifier.fillMaxWidth()) {
                    Text(text = "50g", Modifier.width(xx.dp), textAlign = TextAlign.End)
    
                    Text(
                        text = "Very long text long text",
                        Modifier
                            .padding(start=4.dp)
                            .weight(1f),   
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis
                    )
                    Text(text = "75%", Modifier.width(xx.dp), textAlign = TextAlign.End)
                }
            }
    

    enter image description here