【问题标题】:How to move a word in an android textview above the next word using text span?如何使用文本跨度将 android textview 中的单词移动到下一个单词上方?
【发布时间】:2019-07-23 23:29:13
【问题描述】:

我正在尝试将一个单词移动到 android textview 中的下一个单词上方,就像附加的图像 (example image) 中一样。我已经设法使用 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
}

【问题讨论】:

  • 屏幕截图显示了您已完成的操作或您的预期?
  • 您好,欢迎来到 SO,请阅读 How do I ask a good question? 并提供代码。否则你的问题可能会被否决而得不到回答。
  • 你可能应该在 html 中定义它,然后将 textview 内容设置为 html
  • 您好! @VirRajpurohit 屏幕截图显示了我的预期。我提供了迄今为止编写的函数来处理字符串,并更新了图像以更清晰。
  • 你好@Frieder!我已经提供了我编写的用于处理字符串的函数。

标签: android kotlin textview spannablestringbuilder


【解决方案1】:

您可以在 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));
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2020-12-23
      • 1970-01-01
      • 1970-01-01
      • 2017-10-12
      • 1970-01-01
      • 1970-01-01
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多