package com.xing.expandabletextview
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.widget.RelativeLayout
import android.widget.TextView
class ExpandableTextView @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null,
defStyleAttr: Int = 0,
) : RelativeLayout(context, attributeSet, defStyleAttr) {
private var isExpand = true
private var mMaxLineWhenCollapsed = 3
var mContentTextView: TextView = TextView(context)
private lateinit var mExpandCollapseView: View
private var shouldCollapseInNewLine = false
private var mOnExpandCollapseChangeListener: ((Boolean) -> Unit)? = null
init {
val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.ExpandableTextView)
mMaxLineWhenCollapsed = typedArray.getInt(R.styleable.ExpandableTextView_maxLinesWhenCollapsed, 1)
if (mMaxLineWhenCollapsed < 1) {
mMaxLineWhenCollapsed = 1
}
typedArray.recycle()
}
override fun onFinishInflate() {
super.onFinishInflate()
if (childCount > 1) {
throw IllegalArgumentException("the ExpandableTextView's children should not more than 1")
}
mExpandCollapseView = getChildAt(0)
mExpandCollapseView.setOnClickListener {
toggle()
}
addView(mContentTextView, 0)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val mode = MeasureSpec.getMode(heightMeasureSpec)
var height = MeasureSpec.getSize(heightMeasureSpec)
val lineCount = mContentTextView.layout.lineCount
mExpandCollapseView.visibility = if (lineCount > mMaxLineWhenCollapsed) View.VISIBLE else View.GONE
if (lineCount > mMaxLineWhenCollapsed) {
if (isExpand) {
if (mode != MeasureSpec.EXACTLY) {
val lastLineWidth = mContentTextView.layout.getLineWidth(lineCount - 1)
val lastLineEmpty = measuredWidth - lastLineWidth
shouldCollapseInNewLine = lastLineEmpty < mExpandCollapseView.measuredWidth
height = if (shouldCollapseInNewLine) {
mContentTextView.measuredHeight + mExpandCollapseView.measuredHeight
} else {
mContentTextView.measuredHeight
}
}
setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
} else {
setMeasuredDimension(widthMeasureSpec, mContentTextView.layout.getLineBottom(mMaxLineWhenCollapsed - 1))
}
}
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
if (changed) {
val lastLine = if (isExpand) mContentTextView.lineCount - 1 else mMaxLineWhenCollapsed - 1
val lastLineTop = mContentTextView.layout.getLineTop(lastLine)
val lastLineBottom = mContentTextView.layout.getLineBottom(lastLine)
val offsetY = (lastLineBottom - lastLineTop - mExpandCollapseView.measuredHeight) / 2
if (isExpand) {
val bottom = if (shouldCollapseInNewLine) {
mContentTextView.bottom + mExpandCollapseView.height - offsetY
} else {
mContentTextView.bottom - offsetY
}
mExpandCollapseView.layout(
measuredWidth - mExpandCollapseView.measuredWidth,
bottom - mExpandCollapseView.measuredHeight,
measuredWidth,
bottom
)
} else {
mExpandCollapseView.layout(
measuredWidth - mExpandCollapseView.measuredWidth,
lastLineBottom - mExpandCollapseView.measuredHeight - offsetY,
measuredWidth,
lastLineBottom - offsetY
)
for (line in 0 until mMaxLineWhenCollapsed) {
val lineend = mContentTextView.layout.getLineEnd(line)
Log.e("debug", "lineEnd = $lineend")
}
var lastEndIndex = mContentTextView.layout.getLineEnd(mMaxLineWhenCollapsed - 1) - 1
val lastStartIndex = mContentTextView.layout.getLineStart(mMaxLineWhenCollapsed - 1)
Log.e("dcsdcs", "c----->" + mContentTextView.layout.getPrimaryHorizontal(lastEndIndex - 1))
Log.e("ddd", "lastIndex = $lastEndIndex")
var endIndex = lastEndIndex
while (endIndex > lastStartIndex) {
val xAxis = mContentTextView.layout.getSecondaryHorizontal(endIndex)
if (xAxis < mExpandCollapseView.left) {
break
}
endIndex--
}
for (i in endIndex until lastEndIndex) {
mContentTextView.text.get(i)
}
Log.e("ddd", "lastIndex2 = $lastEndIndex")
}
}
}
private fun toggle() {
isExpand = !isExpand
mOnExpandCollapseChangeListener?.invoke(isExpand)
requestLayout()
}
fun setText(text: CharSequence) {
mContentTextView.text = text
}
fun setOnExpandCollapseChangedListener(listener: (Boolean) -> Unit) {
this.mOnExpandCollapseChangeListener = listener
}
}