带下划线的TextView不能在Android中使用SpannableString换行到下一行

问题描述 投票:2回答:1

我使用此Dotted underline in TextView using SpannableString in Android完成了带下划线的下划线文本视图。但虚线下划线textview不换行到下一行。我已附上截图以供参考。请提出您的想法。谢谢

class DottedUnderlineSpan(mColor: Int, private val mSpan: String) : ReplacementSpan() {
    private val paint: Paint
    private var width: Int = 0
    private var spanLength: Float = 0f
    private val lengthIsCached = false
    internal var strokeWidth: Float = 0f
    internal var dashPathEffect: Float = 0f
    internal var offsetY: Float = 0f

    init {
        strokeWidth = 5f
        dashPathEffect = 4f
        offsetY = 14f
        paint = Paint()
        paint.color = mColor
        paint.style = Paint.Style.STROKE
        paint.pathEffect = DashPathEffect(floatArrayOf(dashPathEffect, dashPathEffect), 0f)
        paint.strokeWidth = strokeWidth
    }

    override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
        width = paint.measureText(text, start, end).toInt()
        return width
    }

    override fun draw(canvas: Canvas, text: CharSequence, start: Int,
                      end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        canvas.drawText(text, start, end, x, y.toFloat(), paint)

        if (!lengthIsCached)
            spanLength = paint.measureText(mSpan)
        val path = Path()
        path.moveTo(x, y + offsetY)
        path.lineTo(x + spanLength, y + offsetY)
        canvas.drawPath(path, this.paint)
    }
}

*使用SpannableStringbuilder设置虚线*

   DottedUnderlineSpan dottedUnderlineSpan = new DottedUnderlineSpan(underlineColor, dottedString);
                    strBuilder.setSpan(dottedUnderlineSpan, start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE);

错误:

Error facing here预期:Expected

android textview spannablestring underline
1个回答
0
投票

问题在于,ReplacementSpan无法跨越线边界。有关此问题的更多信息,请参见Drawing a rounded corner background on text

您可以使用上述博客文章中的解决方案,但我们可以根据您的要求简化该解决方案,如下所示:

这是一般步骤:

  1. Place Annotation跨我们要加下划线的TextView中的文本。
  2. 让文本进行布局,并在使用预绘制侦听器进行绘制之前捕获TextView。此时,文本将被布局,因为它将显示在屏幕上。
  3. 用一个或多个DottedUnderlineSpans替换每个Annotation跨度,以确保每个下划线跨度不跨越线边界。
  4. 替换TextView中的文本。

有点复杂,但是将允许使用DottedUnderlineSpan类。这是结果的图像:

enter image description here

MainActivity.kt

class MainActivity : AppCompatActivity() {  

    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_main)  

        val textView = findViewById<TextView>(R.id.spannedText)  
        val spannableString = SpannableString(textView.text)  
        setAnnotation(spannableString, "production or conversion cycle")  
        setAnnotation(spannableString, "goods")  
        setAnnotation(spannableString, "materials")  
        setAnnotation(spannableString, "into")  
        setAnnotation(spannableString, "goods")  
        setAnnotation(spannableString, "production and conversion cycle, where raw materials are transformed")  
        textView.text = spannableString  

        // Let the layout proceed and catch processing before drawing occurs to add underlines.  
  textView.viewTreeObserver.addOnPreDrawListener {  
  replaceAnnotations(textView)  
        }  
  }  

    private fun setAnnotation(spannableString: SpannableString, subStringToUnderline: String) {  
        val dottedAnnotation = Annotation(ANNOTATION_FOR_UNDERLINE, "don't care")  
        val start = spannableString.indexOf(subStringToUnderline)  
        val end = start + subStringToUnderline.length  
  spannableString.setSpan(dottedAnnotation, start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE)  
    }  

    private fun replaceAnnotations(textView: TextView): Boolean {  
        val text = SpannableString(textView.text)  
        val spans = text.getSpans(0, text.length, Annotation::class.java)  
        return if (spans.isEmpty()) {  
            // All annotation spans have been removed, so let drawing proceed.  
  true  
  } else {  
            val layout = textView.layout  
  spans.forEach { span ->  
  if (span.key == ANNOTATION_FOR_UNDERLINE) {  
                    replaceAnnotationSpan(text, span, layout)  
                }  
            }  
  textView.setText(text, TextView.BufferType.SPANNABLE)  
            // false = need to relayout and redraw.  
  false  
  }  
    }  

    private fun replaceAnnotationSpan(text: SpannableString, span: Annotation, layout: Layout) {  
        val spanStart = text.getSpanStart(span)  
        val spanEnd = text.getSpanEnd(span)  
        val startLine = layout.getLineForOffset(spanStart)  
        val endLine = layout.getLineForOffset(spanEnd)  
        for (line in startLine..endLine) {  
            val lineStart = layout.getLineStart(line)  
            val lineEnd = layout.getLineEnd(line)  
            val segStart = max(spanStart, lineStart)  
            val segEnd = min(spanEnd, lineEnd)  
            val textToUnderline = text.substring(segStart until segEnd)  
            val dottedUnderlineSpan = DottedUnderlineSpan(Color.RED, textToUnderline)  
            text.setSpan(  
                dottedUnderlineSpan,  
                segStart,  
                segEnd,  
                Spanned.SPAN_INCLUSIVE_INCLUSIVE  
  )  
        }  
        text.removeSpan(span)  
    }  

    companion object {  
        const val ANNOTATION_FOR_UNDERLINE = "dottedUnderlineSpan"  
  }  
}

activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout 
  android:layout_width="match_parent"  
  android:layout_height="match_parent"  
  tools:context=".MainActivity">  

    <TextView  
  android:id="@+id/spannedText"  
  android:layout_width="200dp"  
  android:layout_height="wrap_content"  
  android:layout_marginTop="32dp"  
  android:paddingBottom="2dp"  
  android:text="@string/text_to_underline"  
  app:layout_constraintEnd_toEndOf="parent"  
  app:layout_constraintStart_toStartOf="parent"  
  app:layout_constraintTop_toTopOf="parent" />  

</androidx.constraintlayout.widget.ConstraintLayout>
© www.soinside.com 2019 - 2024. All rights reserved.