You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Compose如何显示包含img标签的HTML文本?

解决方案:在Compose中展示带图片的HTML文本

要在Compose里展示包含带宽高参数的<img>标签的HTML文本,核心是通过HtmlCompat.fromHtml的ImageGetter接口自定义图片加载逻辑,结合Coil/Glide实现图片加载,并解析HTML中的图片宽高参数来正确显示。

步骤1:添加依赖

先在模块的build.gradle中添加所需依赖:

// Jsoup:用于解析HTML中的图片宽高属性
implementation "org.jsoup:jsoup:1.17.2"
// Coil:图片加载库(如果用Glide则替换为Glide依赖)
implementation "io.coil-kt:coil:2.5.0"
// Glide(可选,替换Coil时使用)
// implementation "com.github.bumptech.glide:glide:4.16.0"
// annotationProcessor "com.github.bumptech.glide:compiler:4.16.0"

步骤2:解析HTML中的图片宽高

用Jsoup解析HTML,提取所有<img>标签的src、width和height,存储到Map中供后续使用:

private fun extractImageDimensions(html: String): Map<String, Pair<Int, Int>> {
    val imageDimensionMap = mutableMapOf<String, Pair<Int, Int>>()
    val doc = Jsoup.parse(html)
    
    doc.select("img").forEach { imgElement ->
        val src = imgElement.attr("src")
        val width = imgElement.attr("width").takeIf { it.isNotEmpty() }?.toIntOrNull() ?: 0
        val height = imgElement.attr("height").takeIf { it.isNotEmpty() }?.toIntOrNull() ?: 0
        
        if (src.isNotEmpty() && (width > 0 || height > 0)) {
            imageDimensionMap[src] = Pair(width, height)
        }
    }
    return imageDimensionMap
}

步骤3:自定义ImageGetter加载图片

用Coil实现

class CoilImageGetter(
    private val context: Context,
    private val textView: TextView,
    private val imageDimensions: Map<String, Pair<Int, Int>>
) : Html.ImageGetter {
    override fun getDrawable(source: String): Drawable {
        // 占位图,避免TextView布局异常
        val placeholder = BitmapDrawable(context.resources, Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
        placeholder.setBounds(0, 0, 0, 0)
        
        // 获取预设的宽高参数
        val (targetWidth, targetHeight) = imageDimensions[source] ?: Pair(0, 0)
        
        // Coil加载图片
        ImageLoader(context).enqueue(
            ImageRequest.Builder(context)
                .data(source)
                .target(object : Target {
                    override fun onSuccess(result: Drawable) {
                        val scaledDrawable = result.mutate()
                        // 优先使用HTML中定义的宽高,否则用图片原始尺寸
                        val drawableWidth = if (targetWidth > 0) targetWidth else result.intrinsicWidth
                        val drawableHeight = if (targetHeight > 0) targetHeight else result.intrinsicHeight
                        
                        scaledDrawable.setBounds(0, 0, drawableWidth, drawableHeight)
                        // 刷新TextView显示图片
                        textView.text = textView.text
                    }

                    override fun onStart(placeholder: Drawable?) {}
                    override fun onError(error: Drawable?) {}
                })
                .build()
        )
        return placeholder
    }
}

用Glide实现(可选)

class GlideImageGetter(
    private val context: Context,
    private val textView: TextView,
    private val imageDimensions: Map<String, Pair<Int, Int>>
) : Html.ImageGetter {
    override fun getDrawable(source: String): Drawable {
        val placeholder = BitmapDrawable(context.resources, Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
        placeholder.setBounds(0, 0, 0, 0)
        
        val (targetWidth, targetHeight) = imageDimensions[source] ?: Pair(0, 0)
        
        Glide.with(context)
            .load(source)
            .into(object : CustomTarget<Drawable>() {
                override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
                    val scaledDrawable = resource.mutate()
                    val drawableWidth = if (targetWidth > 0) targetWidth else resource.intrinsicWidth
                    val drawableHeight = if (targetHeight > 0) targetHeight else resource.intrinsicHeight
                    
                    scaledDrawable.setBounds(0, 0, drawableWidth, drawableHeight)
                    textView.text = textView.text
                }

                override fun onLoadCleared(placeholder: Drawable?) {}
            })
        return placeholder
    }
}

步骤4:实现Compose组件

修改原有的HtmlText组件,整合上述逻辑:

@Composable
fun HtmlTextWithImages(
    html: String,
    modifier: Modifier = Modifier
) {
    // 缓存图片宽高解析结果,仅在HTML变化时重新解析
    val imageDimensions = remember(html) { extractImageDimensions(html) }

    AndroidView(
        modifier = modifier,
        factory = { context -> TextView(context) },
        update = { textView ->
            val imageGetter = CoilImageGetter(context, textView, imageDimensions)
            // 如果用Glide,替换为GlideImageGetter(context, textView, imageDimensions)
            
            val spannedText = HtmlCompat.fromHtml(
                html,
                HtmlCompat.FROM_HTML_MODE_COMPACT,
                imageGetter,
                null
            )
            
            textView.apply {
                text = spannedText
                textAlignment = TextAlignment.JUSTIFY
                isSingleLine = false
                maxLines = Int.MAX_VALUE
                // 可根据需求添加其他TextView属性,比如文字大小、颜色等
            }
        }
    )
}

注意事项

  1. 网络权限:加载网络图片时,需在AndroidManifest.xml中添加权限:
    <uses-permission android:name="android.permission.INTERNET" />
    
  2. 宽高单位:HTML中的width和height默认是像素,若使用百分比等其他单位,需额外编写转换逻辑。
  3. 自定义样式:可在textView.apply中添加文字样式、 padding 等属性,适配你的UI需求。

内容的提问来源于stack exchange,提问作者MrArtyD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 13:55:24