8

I'm trying to draw text using Canvas and have found that using StaticLayout would take care of the line breaks automatically. I also want to limit its height so that when text is too long it would be ellipsized, but the size of text container is dynamic. I can easily apply the width to StaticLayout, but cant find a way to do height.

I tried to utilize TextUtils.ellipsize(), but having issue to get the spacing between lines.

Onik
  • 15,592
  • 10
  • 57
  • 79
user1865027
  • 2,915
  • 4
  • 26
  • 56

2 Answers2

0

You (and @Cheok Yan Cheng) might try to make use of PagedTextView. The view is intended for Paginating text in Android.

The view partially solves the problem, i.e. reacts to dynamic size changes. As to text ellipsizing, you might achieve this by customising the algorithm I've used for measuring text in height.

Onik
  • 15,592
  • 10
  • 57
  • 79
0

PerracoLabs has the right answer but, as CheokYanCheng stated, the calculation of the maximum number of lines is off (although it may yield the correct result many if not most of the time).

A maximum height cannot be specified for a StaticLayout except indirectly by specifying the maximum number of lines. Ellipsis is tied to the maximum line count anyway, so determining the maximum number of lines for a specific height to back into a solution is the way to go. So, how do we determine the appropriate maximum line count so that a fixed-size StaticLayout with ellipsis can be created as PerracoLabs has explained.?

If the text has no spans that effect the height any of the lines of text then a simply calculation can determine the maximum number of lines that will fit into a StaticLayout before ellipsis.

The following Kotlin function will determine how many lines of text will fit into a view that has a fixed height and width. It is assumed that each line of a StaticLayout has a set height (no height-effecting spans). The top line has the same height of other lines but it is augmented by a top padding. The bottom line has a bottom padding added to it.

private fun getMaxLines(maxHeight: Int): Int {  
    // Build a dummy StaticLayout to get the internal measurements.  
    return makeStaticLayout("", width, 1).run {  
        val lineHeight = getLineBottom(0) - getLineTop(0) + topPadding - bottomPadding  
        (maxHeight - topPadding - bottomPadding) / lineHeight  
}  

However, if the text contains a span that changes the height of one or more lines then the only way to calculate the maximum number of lines is through the creation of the static layout that holds the entire text (no ellipsis) followed by an inspection of the lines within the layout to determine how many complete lines have fit. A new StaticLayout can then be created with the calculated maximum lines determined from the inspection.

The following Kotlin function will calculate the maximum lines by inspecting the StaticLayout for the last full line that is present.

private fun getMaxLinesByInspection(staticLayout: StaticLayout, maxHeight: Int): Int {
    var line = staticLayout.lineCount - 1
    while (line >= 0 && staticLayout.getLineBottom(line) >= maxHeight) {
        line--
    }
    return line + 1
}

I have posted a small project on GitHub as a demonstration.

Here is a screen shot of the app.

enter image description here

Cheticamp
  • 50,205
  • 8
  • 64
  • 109