@@ -5,6 +5,7 @@ import android.os.Build
55import android.text.Layout
66import android.text.StaticLayout
77import android.text.TextDirectionHeuristics
8+ import android.text.TextUtils
89import android.util.TypedValue
910import android.view.View
1011import android.view.View.TEXT_ALIGNMENT_CENTER
@@ -14,8 +15,6 @@ import android.view.ViewGroup
1415import android.widget.TextView
1516import androidx.annotation.AttrRes
1617import androidx.annotation.ColorInt
17- import androidx.core.text.TextDirectionHeuristicsCompat
18- import androidx.core.widget.TextViewCompat
1918
2019@ColorInt
2120fun Context.getColorFromAttr (
@@ -27,51 +26,59 @@ fun Context.getColorFromAttr(
2726 return typedValue.data
2827}
2928
30- inline fun <reified LP : ViewGroup.LayoutParams > View.modifyLayoutParams (function : LP .() -> Unit ) {
29+ inline fun <reified LP : ViewGroup.LayoutParams > View.modifyLayoutParams (function : LP .() -> Unit ) {
3130 layoutParams = (layoutParams as LP ).apply { function() }
3231}
3332
3433fun TextView.needsCollapsing (
3534 availableWidthPx : Int ,
3635 maxLines : Int
3736): Boolean {
38- if (availableWidthPx <= 0 || text.isNullOrEmpty()) return false
37+ // Pick the width the TextView will actually respect before draw
38+ val rawWidth = when {
39+ measuredWidth > 0 -> measuredWidth
40+ // if maxWidth is set, we check it
41+ maxWidth in 1 until Int .MAX_VALUE -> minOf(availableWidthPx, maxWidth)
42+ else -> availableWidthPx
43+ }
44+ val contentWidth = (rawWidth - paddingLeft - paddingRight).coerceAtLeast(0 )
45+ if (contentWidth <= 0 || text.isNullOrEmpty()) return false
3946
40- // The exact text that will be drawn (all-caps, password dots …)
4147 val textForLayout = transformationMethod?.getTransformation(text, this ) ? : text
4248
43- // Build a StaticLayout that mirrors this TextView’s wrap rules
49+ val alignment = when (textAlignment) {
50+ TEXT_ALIGNMENT_CENTER -> Layout .Alignment .ALIGN_CENTER
51+ TEXT_ALIGNMENT_VIEW_END , TEXT_ALIGNMENT_TEXT_END -> Layout .Alignment .ALIGN_OPPOSITE
52+ else -> Layout .Alignment .ALIGN_NORMAL
53+ }
54+ val direction = when (textDirection) {
55+ View .TEXT_DIRECTION_FIRST_STRONG_RTL -> TextDirectionHeuristics .FIRSTSTRONG_RTL
56+ View .TEXT_DIRECTION_RTL -> TextDirectionHeuristics .RTL
57+ View .TEXT_DIRECTION_LTR -> TextDirectionHeuristics .LTR
58+ else -> TextDirectionHeuristics .FIRSTSTRONG_LTR
59+ }
60+
4461 val builder = StaticLayout .Builder
45- .obtain(textForLayout, 0 , textForLayout.length, paint, availableWidthPx )
62+ .obtain(textForLayout, 0 , textForLayout.length, paint, contentWidth )
4663 .setIncludePad(includeFontPadding)
4764 .setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
48- .setBreakStrategy(breakStrategy) // API 23+
65+ .setBreakStrategy(breakStrategy)
4966 .setHyphenationFrequency(hyphenationFrequency)
50- .setMaxLines(Int .MAX_VALUE )
51-
52- // Alignment (honours RTL if textAlignment is END/VIEW_END)
53- builder.setAlignment(
54- when (textAlignment) {
55- TEXT_ALIGNMENT_CENTER -> Layout .Alignment .ALIGN_CENTER
56- TEXT_ALIGNMENT_VIEW_END ,
57- TEXT_ALIGNMENT_TEXT_END -> Layout .Alignment .ALIGN_OPPOSITE
58- else -> Layout .Alignment .ALIGN_NORMAL
59- }
60- )
61-
62- // Direction heuristic
63- val dir = when (textDirection) {
64- View .TEXT_DIRECTION_FIRST_STRONG_RTL -> TextDirectionHeuristics .FIRSTSTRONG_RTL
65- View .TEXT_DIRECTION_RTL -> TextDirectionHeuristics .RTL
66- View .TEXT_DIRECTION_LTR -> TextDirectionHeuristics .LTR
67- else -> TextDirectionHeuristics .FIRSTSTRONG_LTR
68- }
69- builder.setTextDirection(dir)
70-
71- builder.setEllipsize(ellipsize)
67+ .setAlignment(alignment)
68+ .setTextDirection(direction)
69+ .setMaxLines(maxLines) // cap at maxLines
70+ .setEllipsize(ellipsize ? : TextUtils .TruncateAt .END ) // compute ellipsis
7271
7372 builder.setJustificationMode(justificationMode)
7473
7574 val layout = builder.build()
76- return layout.lineCount > maxLines
75+
76+ // Fewer than maxLines: definitely no truncation
77+ if (layout.lineCount < maxLines) return false
78+ // (Defensive) more than maxLines: truncated
79+ if (layout.lineCount > maxLines) return true
80+
81+ // Exactly maxLines: truncated if last line is ellipsized or characters were cut
82+ val last = (maxLines - 1 ).coerceAtMost(layout.lineCount - 1 )
83+ return layout.getEllipsisCount(last) > 0 || layout.getLineEnd(last) < textForLayout.length
7784}
0 commit comments