Tuesday, 13 September 2022

Jetpack Compose: See More button is not showing up for the expandable text when the last line of the collapsed text is blank

I have a description text which is long. I'm trying to implement expand/collapse feature for the text. For the feature, I just refer to https://stackoverflow.com/a/68894858/11943929.

So here is what I've done so far:


private const val MINIMIZED_MAX_LINES = 5

@Composable
fun ProductDescription(
    modifier: Modifier = Modifier,
    description: String? = null
) {

    var expanded by rememberSaveable { mutableStateOf(false) }
    val textLayoutResultState = remember { mutableStateOf<TextLayoutResult?>(null) }
    val seeMoreSizeState = remember { mutableStateOf<IntSize?>(null) }
    val seeMoreOffsetState = remember { mutableStateOf<Offset?>(null) }

    val textLayoutResult = textLayoutResultState.value
    val seeMoreSize = seeMoreSizeState.value
    val seeMoreOffset = seeMoreOffsetState.value

    description?.let {

        val productDesc = Jsoup
            .parse(it)
            .wholeText()
            .replace("\\s+--\\s\\s".toRegex(), "\n\n")
            .replace("\\s{3,}".toRegex(), "\n\n")
            .replace("\\n+X".toRegex(), " X")
            .trim()


        var cutText by remember { mutableStateOf<String?>(null) }

        LaunchedEffect(productDesc, expanded, textLayoutResult, seeMoreSize) {
            val lastLineIndex = MINIMIZED_MAX_LINES - 1
            if (!expanded && textLayoutResult != null
                && seeMoreSize != null && lastLineIndex + 1 == textLayoutResult.lineCount
                && textLayoutResult.isLineEllipsized(
                    lastLineIndex
                )
            ) {
                var lastCharIndex = textLayoutResult.getLineEnd(lastLineIndex, visibleEnd = true) + 1
                var charRect: Rect
                do {
                    lastCharIndex -= 1
                    charRect = textLayoutResult.getCursorRect(lastCharIndex)
                } while (
                    charRect.left > textLayoutResult.size.width - seeMoreSize.width
                )
                seeMoreOffsetState.value =
                    Offset(charRect.left, charRect.bottom - seeMoreSize.height)
                cutText = productDesc.substring(startIndex = 0, endIndex = lastCharIndex)
            }
        }
        Column(
            modifier = modifier,
            verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.dimen_8))
        ) {
            Box {
                Text(
                    text = cutText ?: productDesc,
                    maxLines = if (expanded) Int.MAX_VALUE else MINIMIZED_MAX_LINES,
                    overflow = TextOverflow.Ellipsis,
                    style = MaterialTheme.typography.h4,
                    onTextLayout = { textLayoutResultState.value = it },
                    color = RubiBrandsTheme.colors.productDetailsDescriptionColor,
                )
                if (!expanded) {
                    val density = LocalDensity.current
                    Text(
                        text = stringResource(id = R.string.show_more),
                        fontWeight = FontWeight.SemiBold,
                        onTextLayout = { seeMoreSizeState.value = it.size },
                        color = RubiBrandsTheme.colors.textLink,
                        modifier = Modifier
                            .then(
                                if (seeMoreOffset != null)
                                    Modifier.offset(
                                        x = with(density) { seeMoreOffset.x.toDp() },
                                        y = with(density) { seeMoreOffset.y.toDp() })
                                else Modifier
                            )
                            .clickable {
                                expanded = true
                                cutText = null
                            }
                            .alpha(if (seeMoreOffset != null) 1f else 0f)
                    )
                }
            }
            if (expanded) {
                Text(
                    text = stringResource(id = R.string.show_less),
                    fontWeight = FontWeight.SemiBold,
                    color = RubiBrandsTheme.colors.textLink,
                    modifier = Modifier
                        .clickable {
                            expanded = false
                        }
                )
            }
        }
    }
}

This code works as intended except for one case.

The case is if the last line of the cutted text is a blank line the See More button is not showing up. Here is what I mean :

Otherwise, it is working:

I am also adding the text that causing the issue if you want to test:

Micro fiber top layer 
Comfortable for sweating classes 
Machine washable 
Special Asian flowers design
    
173cmx62cmx3mm
    
Asian Flowers


from Jetpack Compose: See More button is not showing up for the expandable text when the last line of the collapsed text is blank

No comments:

Post a Comment