2
Help us understand the problem. What are the problem?

posted at

Spannableを使ったTextViewでEllipsizeを実現する

デフォルトのTextViewはSpannableを使うとEllipsizeがきかなくなるので、TextViewを継承して以下のような子クラスを作ってあげる。
(android:ellipsize="end"かつandroid:maxLinesを設定していないとき相当)

/**
 * SpannableStringを使用していてもEllipsize==Endの挙動をするTextView
 *
 * 参考 : https://www.programmersought.com/article/20037202696/
 */
class SpannableEllipsizeTextView : androidx.appcompat.widget.AppCompatTextView {


    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    /**
     * Note: when spannableString sets the Spannable object to spannableString, use the flag value of Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, otherwise the subsequent concatenated string may not be displayed
     */
    override fun onDraw(canvas: Canvas?) {
        var lineIndex = 0

        while (lineIndex < lineCount) {
            val charSequence = text
            val bottom = layout.getLineBottom(lineIndex)
            if (bottom > measuredHeight) {
                // もし、途中で見切れるor完全に隠れる行があったら、その直前の行に…を追加して以降は切る。
                val lastCharDown = layout.getLineStart(lineIndex) - 1 // 前の行の最後の文字の文全体でのindex。0始まり。
                val spannableStringBuilder = SpannableStringBuilder()
                spannableStringBuilder.append(charSequence.subSequence(0, lastCharDown + 1)).append("...")
                if (charSequence is Spannable) {
                    // ...の部分も色などを整える
                    // 直前の文字に揃える
                    // XXX この部分は、使用されている可能性があるものに合わせてください。
                    // 今回はForegroundColorSpanとRelativeSizeSpanが使用されている可能性があると想定しています。
                    val spanColor = charSequence.getSpans<ForegroundColorSpan>(lastCharDown, lastCharDown + 1)
                    val spanSize = charSequence.getSpans<RelativeSizeSpan>(lastCharDown, lastCharDown + 1)
                    if (spanColor.size > 0) {
                        spannableStringBuilder.setSpan(ForegroundColorSpan(spanColor[0].foregroundColor), // XXX 新しいインスタンスにしないと、元々かかっていた部分がspanがかからなくなってしまうので注意。
                                lastCharDown,
                                lastCharDown + 3,
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                    }
                    if (spanSize.size > 0) {
                        spannableStringBuilder.setSpan(RelativeSizeSpan(spanSize[0].sizeChange),
                                lastCharDown,
                                lastCharDown + 3,
                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                    }
                }
                text = spannableStringBuilder
                break
            }
            lineIndex++
        }

        super.onDraw(canvas)
    }
}

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
2
Help us understand the problem. What are the problem?