1
+ package com.pengxh.kt.lite.utils
2
+
3
+ import android.content.Context
4
+ import android.os.Build
5
+ import android.text.Editable
6
+ import android.text.Html
7
+ import android.text.Spanned
8
+ import android.text.method.LinkMovementMethod
9
+ import android.text.style.ClickableSpan
10
+ import android.text.style.ImageSpan
11
+ import android.view.View
12
+ import android.widget.TextView
13
+ import androidx.lifecycle.Lifecycle
14
+ import androidx.lifecycle.LifecycleOwner
15
+ import androidx.lifecycle.LifecycleRegistry
16
+ import androidx.lifecycle.lifecycleScope
17
+ import com.bumptech.glide.Glide
18
+ import com.pengxh.kt.lite.extensions.getScreenWidth
19
+ import kotlinx.coroutines.Dispatchers
20
+ import kotlinx.coroutines.launch
21
+ import kotlinx.coroutines.withContext
22
+ import org.xml.sax.XMLReader
23
+ import java.util.Locale
24
+
25
+ class HtmlRenderEngine : LifecycleOwner {
26
+ private val kTag = " HtmlRenderEngine"
27
+ private val registry = LifecycleRegistry (this )
28
+ private lateinit var context: Context
29
+ private lateinit var html: String
30
+ private lateinit var textView: TextView
31
+ private lateinit var imageSourceListener: OnGetImageSourceListener
32
+
33
+ /* *
34
+ * 设置上下文
35
+ * */
36
+ fun setContext (context : Context ): HtmlRenderEngine {
37
+ this .context = context
38
+ return this
39
+ }
40
+
41
+ /* *
42
+ * 设置html格式的文本
43
+ * */
44
+ fun setHtmlContent (html : String ): HtmlRenderEngine {
45
+ this .html = html
46
+ return this
47
+ }
48
+
49
+ /* *
50
+ * 设置显示html格式文本的View
51
+ * */
52
+ fun setTargetView (textView : TextView ): HtmlRenderEngine {
53
+ this .textView = textView
54
+ return this
55
+ }
56
+
57
+ /* *
58
+ * 设置html里面图片地址回调监听
59
+ * */
60
+ fun setOnGetImageSourceListener (imageSourceListener : OnGetImageSourceListener ): HtmlRenderEngine {
61
+ this .imageSourceListener = imageSourceListener
62
+ return this
63
+ }
64
+
65
+ fun load () {
66
+ if (html.isBlank()) {
67
+ return
68
+ }
69
+ lifecycleScope.launch(Dispatchers .Main ) {
70
+ textView.movementMethod = LinkMovementMethod .getInstance()
71
+ // 默认不处理图片先这样简单设置
72
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N ) {
73
+ textView.text = Html .fromHtml(html, Html .FROM_HTML_MODE_LEGACY )
74
+ } else {
75
+ textView.text = Html .fromHtml(html)
76
+ }
77
+ withContext(Dispatchers .IO ) {
78
+ val imageGetter = Html .ImageGetter { source ->
79
+ val drawable = Glide .with (context).load(source).submit().get()
80
+ var width = drawable.intrinsicWidth
81
+ var height = drawable.intrinsicHeight
82
+
83
+ // 对图片按比例缩放尺寸
84
+ val scale = context.getScreenWidth() / width.toFloat()
85
+ width = (scale * width).toInt()
86
+ height = (scale * height).toInt()
87
+ drawable.setBounds(0 , 0 , width, height)
88
+ // return
89
+ drawable
90
+ }
91
+
92
+ val htmlText = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N ) {
93
+ Html .fromHtml(
94
+ html, Html .FROM_HTML_MODE_LEGACY , imageGetter, object : Html .TagHandler {
95
+ override fun handleTag (
96
+ opening : Boolean , tag : String ,
97
+ output : Editable , xmlReader : XMLReader
98
+ ) {
99
+ if (tag.lowercase(Locale .getDefault()) == " img" ) {
100
+ val len = output.length
101
+ val images = output.getSpans(
102
+ len - 1 , len, ImageSpan ::class .java
103
+ )
104
+ val imgSource = images[0 ].source ? : return
105
+ output.setSpan(object : ClickableSpan () {
106
+ override fun onClick (widget : View ) {
107
+ imageSourceListener.imageSource(imgSource)
108
+ }
109
+ }, len - 1 , len, Spanned .SPAN_EXCLUSIVE_EXCLUSIVE )
110
+ }
111
+ }
112
+ })
113
+ } else {
114
+ Html .fromHtml(html, imageGetter, object : Html .TagHandler {
115
+ override fun handleTag (
116
+ opening : Boolean , tag : String , output : Editable , xmlReader : XMLReader
117
+ ) {
118
+ if (tag.lowercase(Locale .getDefault()) == " img" ) {
119
+ val len = output.length
120
+ val images = output.getSpans(len - 1 , len, ImageSpan ::class .java)
121
+ val imgSource = images[0 ].source ? : return
122
+ output.setSpan(object : ClickableSpan () {
123
+ override fun onClick (widget : View ) {
124
+ imageSourceListener.imageSource(imgSource)
125
+ }
126
+ }, len - 1 , len, Spanned .SPAN_EXCLUSIVE_EXCLUSIVE )
127
+ }
128
+ }
129
+ })
130
+ }
131
+ withContext(Dispatchers .Main ) {
132
+ textView.text = htmlText
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ interface OnGetImageSourceListener {
139
+ fun imageSource (url : String )
140
+ }
141
+
142
+ override fun getLifecycle (): Lifecycle {
143
+ return registry
144
+ }
145
+ }
0 commit comments