Skip to content

Instantly share code, notes, and snippets.

@oguzhanaslann
Created November 20, 2023 19:59
Show Gist options
  • Select an option

  • Save oguzhanaslann/df2bcd1dabeca5a2f177d2dd3e3bac7c to your computer and use it in GitHub Desktop.

Select an option

Save oguzhanaslann/df2bcd1dabeca5a2f177d2dd3e3bac7c to your computer and use it in GitHub Desktop.
import android.content.Context
import android.text.Layout
import android.text.method.LinkMovementMethod
import android.util.AttributeSet
import android.util.Log
import androidx.appcompat.widget.AppCompatTextView
import com.thelifeco.wannawell.R
import com.thelifeco.wannawell.utils.extension.spannableString
class ExpandableTextView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {
private var isExpanded = false
private var maxCollapsedLines: Int = COLLAPSED_MAX_LINES
private var originalText: CharSequence? = null
var collapsedSuffix: CharSequence = COLLAPSED_SUFFIX
set(value) {
field = value
if (!isExpanded) {
val ellipsisCount = layout?.getEllipsisCount(maxCollapsedLines - 1) ?: return
collapse(layout!!, ellipsisCount)
}
}
var expandedSuffix: CharSequence = EXPANDED_SUFFIX
set(value) {
field = value
if (isExpanded) {
expand()
}
}
init {
movementMethod = LinkMovementMethod.getInstance()
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView)
isExpanded = typedArray.getBoolean(R.styleable.ExpandableTextView_isExpanded, false)
maxCollapsedLines =
typedArray.getInt(R.styleable.ExpandableTextView_maxCollapsedLines, COLLAPSED_MAX_LINES)
collapsedSuffix =
typedArray.getString(R.styleable.ExpandableTextView_collapsedSuffix) ?: COLLAPSED_SUFFIX
expandedSuffix =
typedArray.getString(R.styleable.ExpandableTextView_expandedSuffix) ?: EXPANDED_SUFFIX
typedArray.recycle()
}
fun setFullText(text: CharSequence) {
originalText = text
isExpanded = false
setText(text)
post(::toggleText)
}
fun toggle() {
isExpanded = !isExpanded
toggleText()
}
private fun toggleText() {
val layout = layout ?: return
skipExpandOrCollapseIfAlreadyShortEnough()
val ellipsisCount = layout.getEllipsisCount(lineCount - 1)
val collapsed = collapsedText(layout, ellipsisCount)
val expanded = originalText
if (collapsed == expanded) {
return
}
if (!isExpanded) {
collapse(layout, ellipsisCount)
} else {
expand()
}
}
private fun skipExpandOrCollapseIfAlreadyShortEnough() {
Log.e("TAG", "skipExpandOrCollapseIfAlreadyShortEnough: lineCount $lineCount")
if (lineCount <= maxCollapsedLines) {
text = originalText
return
}
}
private fun expand() {
text = originalText
if (text.isNotBlank()) {
append(
collapsedSuffix.toString()
.spannableString {
onClick { toggle() }
}
)
}
}
private fun collapse(layout: Layout, ellipsisCount: Int) {
text = collapsedText(layout, ellipsisCount)
Log.e("TAG", "toggleText: isExpanded $isExpanded, collapsedText $text")
if (text.isNotBlank()) {
append(
expandedSuffix.toString()
.spannableString { onClick { toggle() } }
)
}
}
private fun collapsedText(
layout: Layout,
ellipsisCount: Int
): CharSequence? {
val collapsePosition = try {
layout.getLineEnd(maxCollapsedLines - 1) - ellipsisCount
} catch (e: IndexOutOfBoundsException) {
-1
}
return when {
collapsePosition < 0 -> originalText
collapsePosition > (originalText?.length ?: 0) -> originalText
else -> originalText?.subSequence(0, collapsePosition)
}
}
fun isExpanded(): Boolean {
return isExpanded
}
companion object {
const val COLLAPSED_MAX_LINES = 2
const val COLLAPSED_SUFFIX = "See more details"
const val EXPANDED_SUFFIX = "See less details"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment