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属性,比如文字大小、颜色等 } } ) }
注意事项
- 网络权限:加载网络图片时,需在
AndroidManifest.xml中添加权限:<uses-permission android:name="android.permission.INTERNET" /> - 宽高单位:HTML中的
width和height默认是像素,若使用百分比等其他单位,需额外编写转换逻辑。 - 自定义样式:可在
textView.apply中添加文字样式、 padding 等属性,适配你的UI需求。
内容的提问来源于stack exchange,提问作者MrArtyD
相关产品推荐
相关产品推荐

