11package com.flyjingfish.formattextview
22
33import android.content.Context
4- import android.graphics.*
4+ import android.graphics.Canvas
5+ import android.graphics.Color
6+ import android.graphics.LinearGradient
7+ import android.graphics.Paint
8+ import android.graphics.Rect
9+ import android.graphics.RectF
10+ import android.graphics.Shader
11+ import android.graphics.Typeface
512import android.graphics.drawable.Drawable
613import android.graphics.drawable.InsetDrawable
714import android.graphics.drawable.LevelListDrawable
@@ -16,7 +23,7 @@ import android.util.LayoutDirection
1623import android.view.View
1724import androidx.annotation.StringRes
1825import androidx.core.text.TextUtilsCompat
19- import java.util.*
26+ import java.util.Locale
2027
2128
2229class FormatTextView : BaseTextView {
@@ -25,8 +32,13 @@ class FormatTextView : BaseTextView {
2532 private var onInflateImageListener: OnInflateImageListener ? = null
2633 private var isClickSpanItem = false
2734 var isSetOnClick = false
35+ private var isDrawGradient = false
2836 private val underLineTexts: ArrayList <LineText > = ArrayList ()
2937 private val deleteLineTexts: ArrayList <LineText > = ArrayList ()
38+ private val gradientTexts: ArrayList <LineText > = ArrayList ()
39+ private val gradientDrawTexts: ArrayList <GradientText > = ArrayList ()
40+ private var formatArgs: Array <out BaseFormat ?>? = null
41+ private var richText: String ? = null
3042
3143 constructor (context: Context ) : super (context)
3244 constructor (context: Context , attrs: AttributeSet ? ) : super (context, attrs)
@@ -45,6 +57,7 @@ class FormatTextView : BaseTextView {
4557 textValue = textValue.replace(" \\ n" .toRegex(), " <br>" )
4658 textValue = textValue.replace(" \\ r" .toRegex(), " <br>" )
4759 val strings = arrayOfNulls<String >(args.size)
60+ var isContainGradient = false
4861 for (i in args.indices) { // %1$s
4962 val start = " <a href=\" $i \" >"
5063 val end = " </a>"
@@ -64,17 +77,29 @@ class FormatTextView : BaseTextView {
6477 value1 = value1?.replace(" \\ n" .toRegex(), " <br>" )
6578 value1 = value1?.replace(" \\ r" .toRegex(), " <br>" )
6679 strings[i] = start + value1 + end
80+ if (formatText.gradient != null ){
81+ isContainGradient = true
82+ }
6783 }
6884
6985
7086 }
7187 val richText = String .format(textValue, * strings)
72-
88+ formatArgs = args
89+ this .richText = richText
90+ isDrawGradient = ! isContainGradient
91+ underLineTexts.clear()
92+ deleteLineTexts.clear()
93+ gradientTexts.clear()
94+ gradientDrawTexts.clear()
7395 text = getCustomStyleHtml(richText, * args)
7496 highlightColor = Color .TRANSPARENT
7597 autoLinkMask = Linkify .WEB_URLS
7698 }
7799
100+ private fun resetText (){
101+ text = richText?.let { formatArgs?.let { it1 -> getCustomStyleHtml(it, * it1) } }
102+ }
78103
79104 fun setFormatText (@StringRes formatTextRes : Int , vararg args : Int ) {
80105 setFormatText(resources.getString(formatTextRes), * args)
@@ -299,11 +324,83 @@ class FormatTextView : BaseTextView {
299324 userDefaultDelete = false
300325
301326 }
327+ // gradient
328+ if (! isDrawGradient && formatText.gradient != null ) {
329+ val textPaint = TextPaint ()
330+ textPaint.textSize = if (textSize > 0 ) sp2px(context, textSize) else getTextSize()
331+ val fm = textPaint.fontMetrics
332+
333+ val gradientText = LineText (
334+ start,
335+ end,
336+ 0 ,
337+ (fm.descent - fm.ascent) / 2 - fm.descent,
338+ 0f
339+ )
340+ gradientTexts.add(gradientText)
341+
342+ }
343+
344+ if (isDrawGradient && gradientDrawTexts.size > 0 ){
345+ for (gradientDrawText in gradientDrawTexts) {
346+ if (gradientDrawText.start>= start && gradientDrawText.end <= end){
347+
348+ val clickableSpan: GradientSpan = object : GradientSpan () {
349+ override fun updateDrawState (ds : TextPaint ) {
350+ super .updateDrawState(ds)
351+ var left= 0f
352+ var top= 0f
353+ var right= 0f
354+ var bottom= 0f
355+ var orientation = formatText.gradient?.orientation
356+ if (orientation == null ){
357+ orientation = Gradient .Orientation .LEFT_TO_RIGHT
358+ }
359+ when (orientation) {
360+ Gradient .Orientation .LEFT_TO_RIGHT -> {
361+ left = gradientDrawText.rectF.left
362+ top = gradientDrawText.rectF.top
363+ right = gradientDrawText.rectF.right
364+ bottom = gradientDrawText.rectF.top
365+ }
366+ Gradient .Orientation .TOP_TO_BOTTOM -> {
367+ left = gradientDrawText.rectF.left
368+ top = gradientDrawText.rectF.top
369+ right = gradientDrawText.rectF.left
370+ bottom = gradientDrawText.rectF.bottom
371+ }
372+ Gradient .Orientation .LEFT_TOP_TO_RIGHT_BOTTOM -> {
373+ left = gradientDrawText.rectF.left
374+ top = gradientDrawText.rectF.top
375+ right = gradientDrawText.rectF.right
376+ bottom = gradientDrawText.rectF.bottom
377+ }
378+ Gradient .Orientation .LEFT_BOTTOM_TO_RIGHT_TOP -> {
379+ left = gradientDrawText.rectF.left
380+ top = gradientDrawText.rectF.bottom
381+ right = gradientDrawText.rectF.right
382+ bottom = gradientDrawText.rectF.top
383+ }
384+ }
385+ val mLinearGradient = formatText.gradient?.gradientColors?.let {
386+ LinearGradient (
387+ left, top, right, bottom,
388+ it, formatText.gradient?.gradientPositions,
389+ Shader .TileMode .CLAMP
390+ )
391+ }
392+ ds.shader = mLinearGradient
393+ }
394+ }
395+ htmlBuilder.setSpan(clickableSpan, gradientDrawText.start, gradientDrawText.end, flags)
396+ }
397+ }
398+ }
302399
303400 val clickableSpan: ClickableSpan = object : FormatClickableSpan (urlSpan) {
304401 override fun updateDrawState (ds : TextPaint ) {
305402 super .updateDrawState(ds)
306- if (formatText.ignorePaintShader){
403+ if (formatText.ignorePaintShader && formatText.gradient == null ){
307404 ds.shader = null
308405 }
309406 // 设置颜色
@@ -361,6 +458,13 @@ class FormatTextView : BaseTextView {
361458 }
362459 }
363460
461+ abstract class GradientSpan : CharacterStyle (), UpdateAppearance {
462+ override fun updateDrawState (ds : TextPaint ) {
463+ ds.color = ds.linkColor
464+ ds.isUnderlineText = false
465+ }
466+ }
467+
364468 override fun setOnClickListener (l : OnClickListener ? ) {
365469// 为了处理ClickableSpan和View.OnClickListener点击事件冲突
366470 super .setOnClickListener {
@@ -382,8 +486,46 @@ class FormatTextView : BaseTextView {
382486 super .onDraw(canvas)
383487 drawUnderline(canvas)
384488 drawDeleteLine(canvas)
489+ getGradient()
385490 }
491+ private fun getGradient () {
492+ if (gradientTexts.size == 0 || isDrawGradient) {
493+ return
494+ }
495+ // 绘制下划线
496+ for (lineText in gradientTexts) {
497+ val map = HashMap <Int ,GradientText >()
498+ for (i in lineText.start until lineText.end) {
499+ val line = layout.getLineForOffset(i)
500+ val bound = getUnderLineBound(i)
501+ val left = bound.left.toFloat()
502+ val top = bound.bottom.toFloat() - lineText.lineTop* 2
503+ val right = bound.right.toFloat()
504+ val bottom = bound.bottom.toFloat()
505+ val rect = RectF (left,top,right, bottom)
506+ if (! map.containsKey(line)){
507+ val gradientText = GradientText (rect,i,i+ 1 )
508+ map[line] = gradientText
509+ }else {
510+ val gradientText = map[line]
511+ val oldRect = gradientText!! .rectF
512+ if (oldRect.left < rect.left){
513+ oldRect.right = right
514+ }else {
515+ oldRect.left = left
516+ }
517+ gradientText.end = i+ 1
518+ }
519+ }
386520
521+ for ((_,value) in map){
522+ gradientDrawTexts.add(value)
523+ }
524+
525+ }
526+ isDrawGradient = true
527+ resetText()
528+ }
387529 private fun drawUnderline (canvas : Canvas ? ) {
388530 if (underLineTexts.size == 0 ) {
389531 return
@@ -476,6 +618,12 @@ class FormatTextView : BaseTextView {
476618 var lineWidth : Float
477619 )
478620
621+ private class GradientText (
622+ var rectF : RectF ,
623+ var start : Int ,
624+ var end : Int ,
625+ )
626+
479627 override fun onMeasure (widthMeasureSpec : Int , heightMeasureSpec : Int ) {
480628 super .onMeasure(widthMeasureSpec, heightMeasureSpec)
481629 if (underLineTexts.size > 0 ) {
0 commit comments