Skip to content

Instantly share code, notes, and snippets.

@juliooa
Last active March 9, 2020 22:48
Show Gist options
  • Save juliooa/45a51cae1307072063f1eba5518b8c10 to your computer and use it in GitHub Desktop.
Save juliooa/45a51cae1307072063f1eba5518b8c10 to your computer and use it in GitHub Desktop.
TextView.setExpandableText
fun TextView.setExpandableText(text: String, maxLines: Int, buttonsOnNewLines: Boolean = false) {
var layoutLaid = false
var layoutDisposable: Disposable? = null
setText(text)
post {
// If more than max lines are to be used for text, use "more-less" clickable
// labels in order to expand and collapse the text
if (lineCount > maxLines) {
// Temporarily set these values on the TextView.
// After layout is laid, we'll be able to compute the number of characters
// truncated from the text in order to add the "More" clickable label.
ellipsize = TextUtils.TruncateAt.END
setMaxLines(maxLines)
layoutDisposable = globalLayouts().subscribeLoggingErrors(onNext = {
if (isLaidOut && !layoutLaid) {
layoutLaid = true
layoutDisposable?.dispose()
val truncatedCharacters = layout.getEllipsisCount(layout.lineCount - 1)
if (truncatedCharacters > 0) {
val more = context.getString(R.string.more)
val less = context.getString(R.string.less)
// Additional characters to cut from text,
// initially was 1 (for the space between the ellipsis and the "More" label),
// but after testing, 5 gives a more correct visual result
val additionalCut = 5
val truncatedText = if (buttonsOnNewLines) {
String.format("%s…\n%s", text.substring(0, text.length - truncatedCharacters - more.length - additionalCut), more)
} else {
String.format("%s… %s", text.substring(0, text.length - truncatedCharacters - more.length - additionalCut), more)
}
val fullText = if (buttonsOnNewLines) {
String.format("%s\n%s", text, less)
} else {
String.format("%s %s", text, less)
}
val truncatedSpannable = SpannableStringBuilder(truncatedText)
val fullSpannable = SpannableStringBuilder(fullText)
truncatedSpannable.setSpan(object : ClickableSpan() {
override fun onClick(widget: View) {
setText(fullSpannable, TextView.BufferType.SPANNABLE)
}
}, truncatedText.length - more.length, truncatedText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
fullSpannable.setSpan(object : ClickableSpan() {
override fun onClick(widget: View) {
setText(truncatedSpannable, TextView.BufferType.SPANNABLE)
}
}, fullText.length - less.length, fullText.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
// Won't be using the ellipsize and maxLines attributes anymore
ellipsize = null
setMaxLines(Integer.MAX_VALUE)
// Start with the truncated text
setText(truncatedSpannable, TextView.BufferType.SPANNABLE)
movementMethod = LinkMovementMethod.getInstance()
}
}
})
}
}
}
@juliooa
Copy link
Author

juliooa commented Mar 9, 2020

Use as
text_description.setExpandableText(bigTextDescription, maxLines = 2, buttonsOnNewLines = true)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment