1

我正在尝试将一个单词移动到 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
}
4

2 回答 2

0

我已经设法使用 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

于 2019-07-23T15:29:13.977 回答
0

您可以在 TextView 中使用 HTML 来实现此目的,例如上标符号的 HTML/CSS 代码将是:

<!DOCTYPE html> 
<html> 
<head> 
<style> 
sup { 
    vertical-align: super; 
    font-size: medium; 
    color: red;
    position: relative; left: -2.5em; top: -0.5em; 
} 
</style> 
</head> 
<body> 
<p>word <sup>topword</sup></p> 
</body> 
</html> 

请注意,这只是一个示例,您需要修复对齐方式。

要在 TextView 中显示它,请使用以下代码:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    textView.setText(Html.fromHtml(htmlVal, Html.FROM_HTML_MODE_COMPACT));
} else { 
    textView.setText(Html.fromHtml(htmlVal));
}
于 2019-07-22T12:46:51.590 回答