1
+ package com.flyjingfish.formattextview
2
+
3
+ import android.R
4
+ import android.content.Context
5
+ import android.text.Html
6
+ import android.text.SpannableStringBuilder
7
+ import android.text.TextPaint
8
+ import android.text.method.LinkMovementMethod
9
+ import android.text.style.ClickableSpan
10
+ import android.text.style.URLSpan
11
+ import android.text.util.Linkify
12
+ import android.util.AttributeSet
13
+ import android.view.View
14
+ import androidx.annotation.StringRes
15
+ import androidx.appcompat.widget.AppCompatTextView
16
+
17
+ class FormatTextView :AppCompatTextView {
18
+
19
+ private var onFormatClickListener: OnFormatClickListener ? = null
20
+ var isClickSpanItem = false
21
+
22
+ constructor (context: Context ? ) : super (context)
23
+ constructor (context: Context ? , attrs: AttributeSet ? ) : super (context, attrs)
24
+ constructor (context: Context ? , attrs: AttributeSet ? , defStyleAttr: Int ) : super (
25
+ context,
26
+ attrs,
27
+ defStyleAttr
28
+ )
29
+
30
+ fun setFormatTextBean (@StringRes formatTextRes : Int , vararg args : FormatText ? ) {
31
+ setFormatTextBean(resources.getString(formatTextRes), * args)
32
+ }
33
+
34
+ fun setFormatTextBean (formatTextValue : String , vararg args : FormatText ? ) {
35
+ val strings = arrayOfNulls<String >(args.size)
36
+ val colors = IntArray (args.size)
37
+ val bolds = BooleanArray (args.size)
38
+ val underlines = BooleanArray (args.size)
39
+ for (i in 0 until args.size) {
40
+ if (args[i] != null ){
41
+ if (args[i]!! .intValue != 0 ) {
42
+ strings[i] = resources.getString(args[i]!! .intValue)
43
+ } else {
44
+ strings[i] = args[i]!! .strValue
45
+ }
46
+ colors[i] = args[i]!! .color
47
+ bolds[i] = args[i]!! .bold
48
+ underlines[i] = args[i]!! .underline
49
+ }
50
+ }
51
+ setFormatText(colors, bolds, underlines, formatTextValue, strings)
52
+ }
53
+
54
+
55
+ fun setFormatText (@StringRes formatTextRes : Int , vararg args : Int ) {
56
+ setFormatText(resources.getString(formatTextRes), * args)
57
+ }
58
+
59
+ fun setFormatText (formatTextValue : String , vararg args : Int ) {
60
+ val formatTexts: Array <FormatText ?> = arrayOfNulls<FormatText >(args.size)
61
+ for (i in 0 until args.size) {
62
+ formatTexts[i] = FormatText (0 , false , false , args[i])
63
+ }
64
+ setFormatTextBean(formatTextValue, * formatTexts)
65
+ }
66
+
67
+ fun setFormatText (@StringRes formatTextRes : Int , vararg args : String ) {
68
+ setFormatText(resources.getString(formatTextRes), * args)
69
+ }
70
+
71
+ fun setFormatText (formatTextValue : String , vararg args : String ) {
72
+ val formatTexts: Array <FormatText ?> = arrayOfNulls<FormatText >(args.size)
73
+ for (i in 0 until args.size) {
74
+ formatTexts[i] = FormatText (0 , false , false , args[i])
75
+ }
76
+ setFormatTextBean(formatTextValue, * formatTexts)
77
+ }
78
+
79
+
80
+ private fun setFormatText (
81
+ colors : IntArray ,
82
+ bolds : BooleanArray ,
83
+ underlines : BooleanArray ,
84
+ @StringRes formatTextRes : Int ,
85
+ args : Array <String ?>
86
+ ) {
87
+ setFormatText(colors, bolds, underlines, resources.getString(formatTextRes), args)
88
+ }
89
+
90
+ private fun setFormatText (
91
+ colors : IntArray ,
92
+ bolds : BooleanArray ,
93
+ underlines : BooleanArray ,
94
+ formatTextValue : String ,
95
+ args : Array <String ?>
96
+ ) {
97
+ val strings = arrayOfNulls<String >(args.size)
98
+ for (i in args.indices) { // %1$s
99
+ var start = " <a href=\" $i \" >"
100
+ var end = " </a>"
101
+ val value = " %" + (i + 1 ) + " \$ s"
102
+ if (bolds[i]) {
103
+ strings[i] = " <b>$value </b>"
104
+ } else {
105
+ strings[i] = value
106
+ }
107
+ strings[i] = start + strings[i] + end
108
+ }
109
+ val formatText = String .format(formatTextValue, * strings as Array <Any ?>)
110
+ val showText = String .format(formatText, * args as Array <Any ?>)
111
+ text = getClickableHtml(showText, colors, underlines)
112
+ highlightColor = resources.getColor(R .color.transparent)
113
+ autoLinkMask = Linkify .WEB_URLS
114
+ }
115
+
116
+ private fun getClickableHtml (
117
+ html : String ,
118
+ colors : IntArray ,
119
+ underlines : BooleanArray
120
+ ): CharSequence? {
121
+ val spannedHtml = Html .fromHtml(html)
122
+ val clickableHtmlBuilder = SpannableStringBuilder (spannedHtml)
123
+ val spans = clickableHtmlBuilder.getSpans(
124
+ 0 , spannedHtml.length,
125
+ URLSpan ::class .java
126
+ )
127
+ for (i in spans.indices) {
128
+ val span = spans[i]
129
+ setLinkClickable(clickableHtmlBuilder, span, colors[i], underlines[i])
130
+ }
131
+ return clickableHtmlBuilder
132
+ }
133
+
134
+ private fun setLinkClickable (
135
+ clickableHtmlBuilder : SpannableStringBuilder ,
136
+ urlSpan : URLSpan ,
137
+ color : Int ,
138
+ underline : Boolean
139
+ ) {
140
+ val start = clickableHtmlBuilder.getSpanStart(urlSpan)
141
+ val end = clickableHtmlBuilder.getSpanEnd(urlSpan)
142
+ val flags = clickableHtmlBuilder.getSpanFlags(urlSpan)
143
+ val clickableSpan: ClickableSpan = object : ClickableSpan () {
144
+ override fun onClick (widget : View ) {
145
+ val url = urlSpan.url
146
+ onFormatClickListener?.onItemClick(url.toInt())
147
+ isClickSpanItem = true
148
+ }
149
+
150
+ override fun updateDrawState (ds : TextPaint ) {
151
+ super .updateDrawState(ds)
152
+ // 设置颜色
153
+ if (color != 0 ) {
154
+ ds.color = resources.getColor(color)
155
+ } else {
156
+ ds.color = currentTextColor
157
+ }
158
+ // 设置是否要下划线
159
+ ds.isUnderlineText = underline
160
+ }
161
+ }
162
+ clickableHtmlBuilder.setSpan(clickableSpan, start, end, flags)
163
+ }
164
+
165
+
166
+ override fun setOnClickListener (l : OnClickListener ? ) {
167
+ // 为了处理ClickableSpan和View.OnClickListener点击事件冲突
168
+ }
169
+
170
+ fun setOnFormatClickListener (onFormatClickListener : OnFormatClickListener ){
171
+ movementMethod = LinkMovementMethod .getInstance()
172
+ this .onFormatClickListener = onFormatClickListener
173
+ super .setOnClickListener {
174
+ if (! isClickSpanItem){
175
+ this @FormatTextView.onFormatClickListener?.onNoItemClick(this @FormatTextView)
176
+ }
177
+ isClickSpanItem = false
178
+ }
179
+ }
180
+
181
+ }
0 commit comments