首页 > 解决方案 > 如何使用文本跨度将 android textview 中的单词移动到下一个单词上方?

问题描述

我正在尝试将一个单词移动到 android textview 中的下一个单词上方,就像附加的图片(示例图片)一样。我已经设法使用 spannablestringbuilder 将单词向上移动(如上标),但我找不到将文本的右侧部分向左移动以填补空白的方法。有谁知道如何做到这一点?

这是我到目前为止编写的函数:

/**
 * Adds clickable spans for words that are contained between "[" and "]"
 *
 * @param imString The string on which to apply clickable spans
 */
private fun addClickablePart(imString: String): SpannableStringBuilder
{
    var string = imString
    val spannableStringBuilder = SpannableStringBuilder((string.replace("[", "")).replace("]", ""))

    var startIndex = string.indexOf("[")

    while (startIndex != -1)
    {
        string = string.replaceFirst("[", "")
        val endIndex = string.indexOf("]", startIndex)
        string = string.replaceFirst("]", "")
        val clickString = string.substring(startIndex, endIndex)

        spannableStringBuilder.setSpan(
            object: ClickableSpan()
            {
                override fun onClick(view: View)
                {
                    HelperFunction.showToast(this@SongActivity, clickString)
                }

                override fun updateDrawState(text: TextPaint)
                {
                    super.updateDrawState(text)
                    text.isUnderlineText = false
                    text.color = ContextCompat.getColor(this@SongActivity, R.color.colorAccent)
                    text.textSize = HelperFunction.spToPx(this@SongActivity, 12).toFloat()
                    text.baselineShift += (text.ascent()).toInt() // move chord upwards
                    text.typeface = Typeface.create(ResourcesCompat.getFont(this@SongActivity, R.font.roboto_mono), Typeface.BOLD) // set text to bold
                }
            },
            startIndex, endIndex, 0)

        startIndex = string.indexOf("[", endIndex)
    }

    return spannableStringBuilder
}

标签: androidkotlintextviewspannablestringbuilder

解决方案


我已经设法使用 ReplacementSpan 解决了这个问题。我在下面发布代码。

这是自定义 ReplacementSpan 类,它在画布上的所需位置绘制单词:

inner class ChordSpan: ReplacementSpan()
{
    override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: FontMetricsInt?): Int
    {
        val mText = text!!.subSequence(start, end).toString().replace("[", "")
        var chordString = ""
        var regularString = mText

        if (mText.contains("]"))
        {
            chordString = mText.substringBefore("]")
            regularString = mText.substringAfter("]")
        }

        val chordStringTextPaint = getChordStringTextPaint(paint)
        val regularStringTextPaint = getRegularStringTextPaint(paint)
        return max(chordStringTextPaint.measureText(chordString), regularStringTextPaint.measureText(regularString)).toInt()
    }

    private fun getChordStringTextPaint(paint: Paint): TextPaint
    {
        val textPaint = TextPaint(paint)
        textPaint.textSize = textPaint.textSize / 1.5F
        textPaint.typeface = Typeface.DEFAULT_BOLD
        textPaint.color = ContextCompat.getColor(this@SongActivity, R.color.colorAccent)
        return textPaint
    }

    private fun getRegularStringTextPaint(paint: Paint): TextPaint
    {
        return TextPaint(paint)
    }

    override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint)
    {
        val mText = text!!.subSequence(start, end).toString().replace("[", "")
        var chordString = ""
        var regularString = mText

        if (mText.contains("]"))
        {
            chordString = mText.substringBefore("]")
            regularString = mText.substringAfter("]")
        }

        val chordStringTextPaint = getChordStringTextPaint(paint)
        val regularStringTextPaint = getRegularStringTextPaint(paint)

        canvas.drawText(chordString, x, y.toFloat(), chordStringTextPaint)
        canvas.drawText(regularString, x, y.toFloat() + (bottom - top) / 2.5F, regularStringTextPaint)
    }
}

这是在孔文本上应用跨度的函数:

private fun formatDisplayOfLyricsWithChords(string: String): SpannableString
{
    val mString = "$string\n\n"
    val endOfStringIndex = mString.length
    val spannableString = SpannableString(mString)
    var startIndex = 0

    while (startIndex != -1 && startIndex != endOfStringIndex)
    {
        var possibleEndIndex = mString.indexOf("[", startIndex + 1)

        if (possibleEndIndex == -1)
        {
            possibleEndIndex = endOfStringIndex + 1
        }

        var endOfRowIndex = mString.indexOf("\n", startIndex + 1)

        if (endOfRowIndex == -1)
        {
            endOfRowIndex = endOfStringIndex + 1
        }

        val endIndex = minOf(possibleEndIndex, endOfRowIndex, endOfStringIndex)

        spannableString.setSpan(ChordSpan(), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

        if (mString[startIndex] == '[')
        {
            val startIndexClick = startIndex
            val endIndexClick = mString.indexOf("]", startIndex + 1)
            val chord = mString.substring(startIndexClick + 1, endIndexClick)

            spannableString.setSpan(
                object: ClickableSpan()
                {
                    override fun onClick(view: View)
                    {
                        handleClickOnChord(chord)
                    }
                },
                startIndexClick, endIndexClick, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        }

        startIndex = endIndex

        if (startIndex == endOfRowIndex)
        {
            startIndex++
        }
    }

    return spannableString
}

我从类似问题的答案中获得灵感:https ://stackoverflow.com/a/24091284/8211969


推荐阅读