在某些情况下,您需要在应用程序中显示格式丰富的文本,例如博客应用程序或Quora之类的应用程序,但Android的内置函数默认情况下不允许显示嵌入式图像,而且它们会在屏幕上显示难看的蓝线在blockquote标记内的任何内容的左侧。这是在TextView中显示HTML以及在Android中显示图像的简单解决方案。请注意,我们将在Android中使用Kotlin语言实现此项目。以下是该应用程序的演示屏幕截图。
先决条件
- 协程库的基本知识。
- 毕加索图书馆的基本知识。
方法
步骤1:创建一个新项目
要在Android Studio中创建新项目,请参阅如何在Android Studio中创建/启动新项目。请注意,选择Kotlin作为编程语言。
步骤2:编码前的专案设定
- 在colors.xml文件中添加一些颜色。此处的颜色用于块引用样式。您可以自由选择不同的颜色。
colors.xml
#0F9D58
#0F9D58
#03DAC5
#878585
activity_main.xml
ImageGetter.kt
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.text.Html
import android.widget.TextView
import com.squareup.picasso.Picasso
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
// Class to download Images which extends [Html.ImageGetter]
class ImageGetter(
private val res: Resources,
private val htmlTextView: TextView
) : Html.ImageGetter {
// Function needs to overridden when extending [Html.ImageGetter] ,
// which will download the image
override fun getDrawable(url: String): Drawable {
val holder = BitmapDrawablePlaceHolder(res, null)
// Coroutine Scope to download image in Background
GlobalScope.launch(Dispatchers.IO) {
runCatching {
// downloading image in bitmap format using [Picasso] Library
val bitmap = Picasso.get().load(url).get()
val drawable = BitmapDrawable(res, bitmap)
// To make sure Images don't go out of screen , Setting width less
// than screen width, You can change image size if you want
val width = getScreenWidth() - 150
// Images may stretch out if you will only resize width,
// hence resize height to according to aspect ratio
val aspectRatio: Float =
(drawable.intrinsicWidth.toFloat()) / (drawable.intrinsicHeight.toFloat())
val height = width / aspectRatio
drawable.setBounds(10, 20, width, height.toInt())
holder.setDrawable(drawable)
holder.setBounds(10, 20, width, height.toInt())
withContext(Dispatchers.Main) {
htmlTextView.text = htmlTextView.text
}
}
}
return holder
}
// Actually Putting images
internal class BitmapDrawablePlaceHolder(res: Resources, bitmap: Bitmap?) :
BitmapDrawable(res, bitmap) {
private var drawable: Drawable? = null
override fun draw(canvas: Canvas) {
drawable?.run { draw(canvas) }
}
fun setDrawable(drawable: Drawable) {
this.drawable = drawable
}
}
// Function to get screenWidth used above
fun getScreenWidth() =
Resources.getSystem().displayMetrics.widthPixels
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Click Listener to listner button click events.
// display_html is button id from activity_main.xml
// You don't need to get reference to it because koltin
// provides synthetic import and
// We are gonna use for the same for rest of Views
display_html.setOnClickListener {
if (editor.text.isNotEmpty()) {
displayHtml(editor.text.toString())
}
}
}
}
Kotlin
private fun displayHtml(html: String) {
// Creating object of ImageGetter class you just created
val imageGetter = ImageGetter(resources, html_viewer)
// Using Html framework to parse html
val styledText=HtmlCompat.fromHtml(html,
HtmlCompat.FROM_HTML_MODE_LEGACY,
imageGetter,null)
// to enable image/link clicking
html_viewer.movementMethod = LinkMovementMethod.getInstance()
// setting the text after formatting html and downloadig and setting images
html_viewer.text = styledText
}
Kotlin
import android.os.Bundle
import android.text.method.LinkMovementMethod
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.HtmlCompat
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Click Listener to listner button click events.
// display_html is button id from activity_main.xml
// You don't need to get reference to it
// because koltin provides synthetic import and
// We are gonna use for the same for rest of Views
display_html.setOnClickListener {
if (editor.text.isNotEmpty()) {
displayHtml(editor.text.toString())
}
}
}
private fun displayHtml(html: String) {
// Creating object of ImageGetter class you just created
val imageGetter = ImageGetter(resources, html_viewer)
// Using Html framework to parse html
val styledText =
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY, imageGetter,null)
// to enable image/link clicking
html_viewer.movementMethod = LinkMovementMethod.getInstance()
// setting the text after formatting html and downloadig and setting images
html_viewer.text = styledText
}
}
QuoteSpanClass.kt
import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.style.LeadingMarginSpan
import android.text.style.LineBackgroundSpan
class QuoteSpanClass(
private val backgroundColor: Int,
private val stripeColor: Int,
private val stripeWidth: Float,
private val gap: Float
) : LeadingMarginSpan, LineBackgroundSpan {
// Margin for the block quote tag
override fun getLeadingMargin(first: Boolean): Int {
return (stripeWidth + gap).toInt()
}
// this function draws the margin.
override fun drawLeadingMargin(
c: Canvas,
p: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
first: Boolean,
layout: Layout
) {
val style = p.style
val paintColor = p.color
p.style = Paint.Style.FILL
p.color = stripeColor
// Creating margin according to color and stripewidth it recieves
// Press CTRL+Q on function name to read more
c.drawRect(x.toFloat(), top.toFloat(), x + dir * stripeWidth, bottom.toFloat(), p)
p.style = style
p.color = paintColor
}
override fun drawBackground(
c: Canvas,
p: Paint,
left: Int,
right: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
lnum: Int
) {
val paintColor = p.color
p.color = backgroundColor
// It draws the background on which blockquote text is written
c.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), p)
p.color = paintColor
}
}
MainActivity.kt
private fun replaceQuoteSpans(spannable: Spannable)
{
val quoteSpans: Array =
spannable.getSpans(0, spannable.length - 1, QuoteSpan::class.java)
for (quoteSpan in quoteSpans)
{
val start: Int = spannable.getSpanStart(quoteSpan)
val end: Int = spannable.getSpanEnd(quoteSpan)
val flags: Int = spannable.getSpanFlags(quoteSpan)
spannable.removeSpan(quoteSpan)
spannable.setSpan(
QuoteSpanClass(
// background color
ContextCompat.getColor(this, R.color.colorPrimary),
// strip color
ContextCompat.getColor(this, R.color.colorAccent),
// strip width
10F, 50F
),
start, end, flags
)
}
}
Kotlin
import android.os.Bundle
import android.text.Spannable
import android.text.method.LinkMovementMethod
import android.text.style.QuoteSpan
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Click Listener to listner button click events.
// display_html is button id from activity_main.xml
// You don't need to get reference to it
// because koltin provides synthetic import and
// We are gonna use for the same for rest of Views
display_html.setOnClickListener {
if (editor.text.isNotEmpty()) {
displayHtml(editor.text.toString())
}
}
}
private fun displayHtml(html: String) {
// Creating object of ImageGetter class you just created
val imageGetter = ImageGetter(resources, html_viewer)
// Using Html framework to parse html
val styledText =
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY, imageGetter,null)
replaceQuoteSpans(styledText as Spannable)
// setting the text after formatting html and downloadig and setting images
html_viewer.text = styledText
// to enable image/link clicking
html_viewer.movementMethod = LinkMovementMethod.getInstance()
}
private fun replaceQuoteSpans(spannable: Spannable)
{
val quoteSpans: Array =
spannable.getSpans(0, spannable.length - 1, QuoteSpan::class.java)
for (quoteSpan in quoteSpans)
{
val start: Int = spannable.getSpanStart(quoteSpan)
val end: Int = spannable.getSpanEnd(quoteSpan)
val flags: Int = spannable.getSpanFlags(quoteSpan)
spannable.removeSpan(quoteSpan)
spannable.setSpan(
QuoteSpanClass(
// background color
ContextCompat.getColor(this, R.color.colorPrimary),
// strip color
ContextCompat.getColor(this, R.color.colorAccent),
// strip width
10F, 50F
),
start, end, flags
)
}
}
}
Kotlin
// Function to parse image tags and enable click events
fun ImageClick(html: Spannable)
{
for (span in html.getSpans(0, html.length, ImageSpan::class.java))
{
val flags = html.getSpanFlags(span)
val start = html.getSpanStart(span)
val end = html.getSpanEnd(span)
html.setSpan(object : URLSpan(span.source) {
override fun onClick(v: View) {
Log.d(TAG, "onClick: url is ${span.source}")
}
}, start, end, flags)
}
Kotlin
import android.os.Bundle
import android.text.Spannable
import android.text.method.LinkMovementMethod
import android.text.style.ImageSpan
import android.text.style.QuoteSpan
import android.text.style.URLSpan
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat
import kotlinx.android.synthetic.main.activity_main.*
private const val TAG="MainActivity"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Click Listener to listner button click events.
// display_html is button id from activity_main.xml
// You don't need to get reference to it
// because koltin provides synthetic import and
// We are gonna use for the same for rest of Views
display_html.setOnClickListener {
if (editor.text.isNotEmpty()) {
displayHtml(editor.text.toString())
}
}
}
private fun displayHtml(html: String) {
// Creating object of ImageGetter class you just created
val imageGetter = ImageGetter(resources, html_viewer)
// Using Html framework to parse html
val styledText =
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY, imageGetter, null)
replaceQuoteSpans(styledText as Spannable)
ImageClick(styledText as Spannable)
// setting the text after formatting html and downloadig and setting images
html_viewer.text = styledText
// to enable image/link clicking
html_viewer.movementMethod = LinkMovementMethod.getInstance()
}
private fun replaceQuoteSpans(spannable: Spannable) {
val quoteSpans: Array =
spannable.getSpans(0, spannable.length - 1, QuoteSpan::class.java)
for (quoteSpan in quoteSpans) {
val start: Int = spannable.getSpanStart(quoteSpan)
val end: Int = spannable.getSpanEnd(quoteSpan)
val flags: Int = spannable.getSpanFlags(quoteSpan)
spannable.removeSpan(quoteSpan)
spannable.setSpan(
QuoteSpanClass(
// background color
ContextCompat.getColor(this, R.color.colorPrimary),
// strip color
ContextCompat.getColor(this, R.color.colorAccent),
// strip width
10F, 50F
),
start, end, flags
)
}
}
// Function to parse image tags and enable click events
fun ImageClick(html: Spannable) {
for (span in html.getSpans(0, html.length, ImageSpan::class.java)) {
val flags = html.getSpanFlags(span)
val start = html.getSpanStart(span)
val end = html.getSpanEnd(span)
html.setSpan(object : URLSpan(span.source) {
override fun onClick(v: View) {
Log.d(TAG, "onClick: url is ${span.source}")
}
}, start, end, flags)
}
}
}
- 转到build.gradle(Module:app)文件并添加以下依赖项。
// Picasso library to downlowding images
implementation ‘com.squareup.picasso:picasso:2.71828’
// Coroutines dependency to put the downloading process in background thread
implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9’
步骤3:使用activity_main.xml文件
在以下activity_main.xml文件中,我们添加了以下小部件:
- 用户将在其中输入HTML文本的EditText,
- 触发事件以显示HTML文本的按钮,
- ScrollView可以平滑滚动,
- TextView在处理输入后显示HTML。
activity_main.xml
输出界面:
第4步:创建Kotlin类ImageGetter.kt
创建一个类,将下载img中包含的图像 标签。以下是完整的ImageGetter.kt文件。通过参考代码中的相应注释来理解完整的代码。
ImageGetter.kt
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.text.Html
import android.widget.TextView
import com.squareup.picasso.Picasso
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
// Class to download Images which extends [Html.ImageGetter]
class ImageGetter(
private val res: Resources,
private val htmlTextView: TextView
) : Html.ImageGetter {
// Function needs to overridden when extending [Html.ImageGetter] ,
// which will download the image
override fun getDrawable(url: String): Drawable {
val holder = BitmapDrawablePlaceHolder(res, null)
// Coroutine Scope to download image in Background
GlobalScope.launch(Dispatchers.IO) {
runCatching {
// downloading image in bitmap format using [Picasso] Library
val bitmap = Picasso.get().load(url).get()
val drawable = BitmapDrawable(res, bitmap)
// To make sure Images don't go out of screen , Setting width less
// than screen width, You can change image size if you want
val width = getScreenWidth() - 150
// Images may stretch out if you will only resize width,
// hence resize height to according to aspect ratio
val aspectRatio: Float =
(drawable.intrinsicWidth.toFloat()) / (drawable.intrinsicHeight.toFloat())
val height = width / aspectRatio
drawable.setBounds(10, 20, width, height.toInt())
holder.setDrawable(drawable)
holder.setBounds(10, 20, width, height.toInt())
withContext(Dispatchers.Main) {
htmlTextView.text = htmlTextView.text
}
}
}
return holder
}
// Actually Putting images
internal class BitmapDrawablePlaceHolder(res: Resources, bitmap: Bitmap?) :
BitmapDrawable(res, bitmap) {
private var drawable: Drawable? = null
override fun draw(canvas: Canvas) {
drawable?.run { draw(canvas) }
}
fun setDrawable(drawable: Drawable) {
this.drawable = drawable
}
}
// Function to get screenWidth used above
fun getScreenWidth() =
Resources.getSystem().displayMetrics.widthPixels
}
Note: One can change the height and the width of the image according to your will in the getDrawable() function.
步骤5:使用MainActivity.kt文件
- 为onCreate()方法内的按钮创建一个click-Listener。
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Click Listener to listner button click events.
// display_html is button id from activity_main.xml
// You don't need to get reference to it because koltin
// provides synthetic import and
// We are gonna use for the same for rest of Views
display_html.setOnClickListener {
if (editor.text.isNotEmpty()) {
displayHtml(editor.text.toString())
}
}
}
}
- 创建一个函数displayHtml() ,您刚刚在点击监听器中调用了该函数。
Note: Kotlin has Synthetic import ,so there was no need of storing reference to views in a variable and we are going to do the same for all other views.
科特林
private fun displayHtml(html: String) {
// Creating object of ImageGetter class you just created
val imageGetter = ImageGetter(resources, html_viewer)
// Using Html framework to parse html
val styledText=HtmlCompat.fromHtml(html,
HtmlCompat.FROM_HTML_MODE_LEGACY,
imageGetter,null)
// to enable image/link clicking
html_viewer.movementMethod = LinkMovementMethod.getInstance()
// setting the text after formatting html and downloadig and setting images
html_viewer.text = styledText
}
- 因此,现在完成了映像工作,现在应该加载映像了。以下是MainActivity.kt文件的完整代码。
科特林
import android.os.Bundle
import android.text.method.LinkMovementMethod
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.HtmlCompat
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Click Listener to listner button click events.
// display_html is button id from activity_main.xml
// You don't need to get reference to it
// because koltin provides synthetic import and
// We are gonna use for the same for rest of Views
display_html.setOnClickListener {
if (editor.text.isNotEmpty()) {
displayHtml(editor.text.toString())
}
}
}
private fun displayHtml(html: String) {
// Creating object of ImageGetter class you just created
val imageGetter = ImageGetter(resources, html_viewer)
// Using Html framework to parse html
val styledText =
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY, imageGetter,null)
// to enable image/link clicking
html_viewer.movementMethod = LinkMovementMethod.getInstance()
// setting the text after formatting html and downloadig and setting images
html_viewer.text = styledText
}
}
- 让我们看看它现在如何工作。使用其他网页的HTML。在这里,我们使用了GFG文章的以下网页。从此处获取HTML字符串文件。
Note: Remember that input the HTML text inside the EditText and then click on the “DISPLAY HTML” Button
输出
图像现在正在加载,但是您可以看到,在blockquote部分中,有一条普通的难看的蓝线。现在,我们将解决此问题。
第6步:创建Kotlin类QuoteSpanClass.kt
创建一个名为QuoteSpanClass的Kotlin类,并将以下代码添加到该文件中。
QuoteSpanClass.kt
import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.style.LeadingMarginSpan
import android.text.style.LineBackgroundSpan
class QuoteSpanClass(
private val backgroundColor: Int,
private val stripeColor: Int,
private val stripeWidth: Float,
private val gap: Float
) : LeadingMarginSpan, LineBackgroundSpan {
// Margin for the block quote tag
override fun getLeadingMargin(first: Boolean): Int {
return (stripeWidth + gap).toInt()
}
// this function draws the margin.
override fun drawLeadingMargin(
c: Canvas,
p: Paint,
x: Int,
dir: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
first: Boolean,
layout: Layout
) {
val style = p.style
val paintColor = p.color
p.style = Paint.Style.FILL
p.color = stripeColor
// Creating margin according to color and stripewidth it recieves
// Press CTRL+Q on function name to read more
c.drawRect(x.toFloat(), top.toFloat(), x + dir * stripeWidth, bottom.toFloat(), p)
p.style = style
p.color = paintColor
}
override fun drawBackground(
c: Canvas,
p: Paint,
left: Int,
right: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
lnum: Int
) {
val paintColor = p.color
p.color = backgroundColor
// It draws the background on which blockquote text is written
c.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), p)
p.color = paintColor
}
}
步骤7:使用MainActivity.kt文件
- 由于创建了QuoteSpanClass,是时候在MainActivity.kt中使用此类了,但是在您需要创建一个函数来解析blockquote标签并使用QuoteSpanClass绘制边距和背景之前,该是时候了。
MainActivity.kt
private fun replaceQuoteSpans(spannable: Spannable)
{
val quoteSpans: Array =
spannable.getSpans(0, spannable.length - 1, QuoteSpan::class.java)
for (quoteSpan in quoteSpans)
{
val start: Int = spannable.getSpanStart(quoteSpan)
val end: Int = spannable.getSpanEnd(quoteSpan)
val flags: Int = spannable.getSpanFlags(quoteSpan)
spannable.removeSpan(quoteSpan)
spannable.setSpan(
QuoteSpanClass(
// background color
ContextCompat.getColor(this, R.color.colorPrimary),
// strip color
ContextCompat.getColor(this, R.color.colorAccent),
// strip width
10F, 50F
),
start, end, flags
)
}
}
- 最后,在设置html_viewer的文本之前,请从displayHtml()函数调用此函数。以下是MainActivity.kt文件的完整代码。
科特林
import android.os.Bundle
import android.text.Spannable
import android.text.method.LinkMovementMethod
import android.text.style.QuoteSpan
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Click Listener to listner button click events.
// display_html is button id from activity_main.xml
// You don't need to get reference to it
// because koltin provides synthetic import and
// We are gonna use for the same for rest of Views
display_html.setOnClickListener {
if (editor.text.isNotEmpty()) {
displayHtml(editor.text.toString())
}
}
}
private fun displayHtml(html: String) {
// Creating object of ImageGetter class you just created
val imageGetter = ImageGetter(resources, html_viewer)
// Using Html framework to parse html
val styledText =
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY, imageGetter,null)
replaceQuoteSpans(styledText as Spannable)
// setting the text after formatting html and downloadig and setting images
html_viewer.text = styledText
// to enable image/link clicking
html_viewer.movementMethod = LinkMovementMethod.getInstance()
}
private fun replaceQuoteSpans(spannable: Spannable)
{
val quoteSpans: Array =
spannable.getSpans(0, spannable.length - 1, QuoteSpan::class.java)
for (quoteSpan in quoteSpans)
{
val start: Int = spannable.getSpanStart(quoteSpan)
val end: Int = spannable.getSpanEnd(quoteSpan)
val flags: Int = spannable.getSpanFlags(quoteSpan)
spannable.removeSpan(quoteSpan)
spannable.setSpan(
QuoteSpanClass(
// background color
ContextCompat.getColor(this, R.color.colorPrimary),
// strip color
ContextCompat.getColor(this, R.color.colorAccent),
// strip width
10F, 50F
),
start, end, flags
)
}
}
}
输出
现在,让我们在输出屏幕中查看更改。
Note: One can change strip Color, background color, strip width of your choice in replaceQuoteSpans function, and beautify it.
步骤8:在MainActivity.kt文件中创建ImageImage()方法
- 但是仍然存在一个问题。如果单击图像怎么办?到目前为止,什么都没有发生。您需要添加几行来处理。
科特林
// Function to parse image tags and enable click events
fun ImageClick(html: Spannable)
{
for (span in html.getSpans(0, html.length, ImageSpan::class.java))
{
val flags = html.getSpanFlags(span)
val start = html.getSpanStart(span)
val end = html.getSpanEnd(span)
html.setSpan(object : URLSpan(span.source) {
override fun onClick(v: View) {
Log.d(TAG, "onClick: url is ${span.source}")
}
}, start, end, flags)
}
- 然后在设置html_viewer的文本之前,从displayHtml()函数调用此函数。以下是MainActivity.kt文件的完整代码。
科特林
import android.os.Bundle
import android.text.Spannable
import android.text.method.LinkMovementMethod
import android.text.style.ImageSpan
import android.text.style.QuoteSpan
import android.text.style.URLSpan
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat
import kotlinx.android.synthetic.main.activity_main.*
private const val TAG="MainActivity"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Click Listener to listner button click events.
// display_html is button id from activity_main.xml
// You don't need to get reference to it
// because koltin provides synthetic import and
// We are gonna use for the same for rest of Views
display_html.setOnClickListener {
if (editor.text.isNotEmpty()) {
displayHtml(editor.text.toString())
}
}
}
private fun displayHtml(html: String) {
// Creating object of ImageGetter class you just created
val imageGetter = ImageGetter(resources, html_viewer)
// Using Html framework to parse html
val styledText =
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY, imageGetter, null)
replaceQuoteSpans(styledText as Spannable)
ImageClick(styledText as Spannable)
// setting the text after formatting html and downloadig and setting images
html_viewer.text = styledText
// to enable image/link clicking
html_viewer.movementMethod = LinkMovementMethod.getInstance()
}
private fun replaceQuoteSpans(spannable: Spannable) {
val quoteSpans: Array =
spannable.getSpans(0, spannable.length - 1, QuoteSpan::class.java)
for (quoteSpan in quoteSpans) {
val start: Int = spannable.getSpanStart(quoteSpan)
val end: Int = spannable.getSpanEnd(quoteSpan)
val flags: Int = spannable.getSpanFlags(quoteSpan)
spannable.removeSpan(quoteSpan)
spannable.setSpan(
QuoteSpanClass(
// background color
ContextCompat.getColor(this, R.color.colorPrimary),
// strip color
ContextCompat.getColor(this, R.color.colorAccent),
// strip width
10F, 50F
),
start, end, flags
)
}
}
// Function to parse image tags and enable click events
fun ImageClick(html: Spannable) {
for (span in html.getSpans(0, html.length, ImageSpan::class.java)) {
val flags = html.getSpanFlags(span)
val start = html.getSpanStart(span)
val end = html.getSpanEnd(span)
html.setSpan(object : URLSpan(span.source) {
override fun onClick(v: View) {
Log.d(TAG, "onClick: url is ${span.source}")
}
}, start, end, flags)
}
}
}
- 现在,如果您单击任何图像并选中Logcat,您将看到正在记录图像的URL。从那里您可以使用该URL触发某些函数。
输出:在模拟器上运行
资源:在此处获取完整的项目文件。