我使用此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);
错误:
问题在于,ReplacementSpan无法跨越线边界。有关此问题的更多信息,请参见Drawing a rounded corner background on text。
您可以使用上述博客文章中的解决方案,但我们可以根据您的要求简化该解决方案,如下所示:
这是一般步骤:
有点复杂,但是将允许使用DottedUnderlineSpan类。这是结果的图像:
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>