Как переместить слово в текстовом представлении Android над следующим словом, используя текстовый диапазон?

Я пытаюсь переместить слово над следующим словом в текстовом представлении Android, как на прикрепленном изображении (пример изображения). Мне удалось сдвинуть слово вверх (например, надстрочный индекс) с помощью 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
}

person Alex    schedule 22.07.2019    source источник
comment
Скриншот показывает, что вы сделали или чего ожидаете?   -  person Vir Rajpurohit    schedule 22.07.2019
comment
Привет и добро пожаловать в SO, прочитайте Как мне задать хороший вопрос? и предоставьте код. В противном случае ваш вопрос, вероятно, будет заминусован и останется без ответа.   -  person Frieder    schedule 22.07.2019
comment
вам, вероятно, следует определить это в html, а затем установить содержимое textview в html   -  person bvk256    schedule 22.07.2019
comment
Привет! @VirRajpurohit скриншот показывал то, что я ожидал. Я предоставил функцию, которую я написал до сих пор, для обработки строки, и я обновил изображение, чтобы оно было более четким.   -  person Alex    schedule 22.07.2019
comment
Привет @Фридер! Я предоставил функцию, которую я написал для обработки строки.   -  person Alex    schedule 22.07.2019
comment
Привет @bvk256! Можете ли вы показать мне пример того, как это можно сделать с помощью html? Я добавил свою функцию для обработки строки и обновил изображение примера, чтобы уточнить, что я ожидаю.   -  person Alex    schedule 22.07.2019


Ответы (2)


Вы можете использовать HTML в своем TextView для достижения этого, например, код 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));
}
person bvk256    schedule 22.07.2019

Мне удалось решить проблему с помощью 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

person Alex    schedule 23.07.2019