原来Span可以这样加载网络图(下),Flutter中网络图片加载和缓存源码分析

}

这里对textView使用弱引用,避免内存泄漏。然后定义一个图片加载接口,用来处理这个请求。

interface DrawableProvider {
fun get(request: URLImageSpanRequest): Drawable
}

这里我用了Glide来加载图片。

class GlideDrawableProvider : DrawableProvider {
override fun get(request: URLImageSpanRequest): Drawable {
val drawable = if (request.url.isNullOrEmpty()) {
request.placeholderDrawable ?: request.errorPlaceholder
} else {
execute(request)
request.placeholderDrawable
}
return drawable ?: ColorDrawable()/Can’t be null/
}

fun execute(request: URLImageSpanRequest) {
val view = request.view ?: return
val span = request.span
Glide.with(view)
.load(request.url)
.error(request.errorPlaceholder)
.override(request.desiredWidth, request.desiredHeight)
.into(object : CustomTarget() {
override fun onResourceReady(
resource: Drawable,
transition: Transition?
) {
resource.setBounds(0, 0, resource.intrinsicWidth, resource.intrinsicHeight)
onResponse(request, resource)
}

override fun onl oadFailed(errorDrawable: Drawable?) {
if (errorDrawable != null) {
onResponse(request, errorDrawable)
}
}

override fun onl oadCleared(placeholder: Drawable?) {
}

private fun onResponse(request: URLImageSpanRequest, drawable: Drawable) {
val spannable = request.view?.text as? Spannable ?: return
spannable.replaceSpan(
span,
ImageSpan(drawable, request.verticalAlignment),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
})
}

fun Spannable.replaceSpan(oldSpan: Any?, newSpan: Any?, flags: Int): Boolean {
if (oldSpan == null || newSpan == null) {
return false
}
val start = getSpanStart(oldSpan)
val end = getSpanEnd(oldSpan)
if (start == -1 || end == -1) {
return false
}
removeSpan(oldSpan)
setSpan(newSpan, start, end, flags)
return true
}
}

如果url是空的话就没必要开启图片加载了。get()方法的返回值是占位图。图片加载后替换占位Span。

最后用Builder模式将上面的内容组合一下

class URLImageSpan {
open class Builder(private val provider: DrawableProvider = GlideDrawableProvider()) {
private var url: String? = null
private var placeholderDrawable: Drawable? = null
private var placeholderId = 0
private var useInstinctPlaceholderSize = true
private var errorPlaceholder: Drawable? = null
private var errorId = 0
private var useInstinctErrorPlaceholderSize = true
private var verticalAlignment = DynamicDrawableSpan.ALIGN_BOTTOM
private var desiredWidth = -1
private var desiredHeight = -1

fun override(width: Int, height: Int): Builder {
this.desiredWidth = width
this.desiredHeight = height
return this
}

fun url(url: String?): Builder {
this.url = url
r原来Span可以这样加载网络图(下),Flutter中网络图片加载和缓存源码分析
eturn this
}

fun placeholder(drawable: Drawable?): Builder {
this.placeholderDrawable = drawable
this.placeholderId = 0
this.useInstinctPlaceholderSize = true
return this
}

fun error(drawable: Drawable?): Builder {
this.errorPlaceholder = drawable
this.errorId = 0
this.useInstinctErrorPlaceholderSize = true
return this
}

fun buildRequest(textView: TextView): URLImageSpanRequest {
val context = textView.context
return URLImageSpanRequest(
textView = textView,
url = url,
placeholderDrawable = getPlaceholderDrawable(context),
errorPlaceholder = getErrorDrawable(context),
verticalAlignment = verticalAlignment,
desiredWidth = desiredWidth,
desiredHeight = desiredHeight
)
}

fun build(textView: TextView): DynamicDrawableSpan {
val request = buildRequest(textView)
return object : DynamicDrawableSpan() {
override fun getDrawable(): Drawable {
request.span = this
return provider.get(request)
}
}
}
}
}

注意记得将占位span赋值。见上面的build()方法

封装后的使用方式

val ss =
SpannableString(“To be or not to be, that is the question(生存还是毁灭,这是一个值得考虑的问题)”)
val urlImageSpan = URLImageSpan.Builder()
.url(“https://sf6-ttcdn-tos.pstatp.com/img/user-avatar/d8111dfb52a63f3f12739194cf367754~500x500.png”)
.override(100.dp, 100.dp)
.build(textView)
ss.setSpan(urlImageSpan, 0, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.setText(ss, TextView.BufferType.SPANNABLE) // 必需设置

与之前的用法对比,明显简单了不少

val ss = SpannableStringBuilder(“To be or not to be, that is the question(生存还是毁灭,这是一个值得考虑的问题)”)
val placeholderSpan = ImageSpan(context, R.mipmap.placeholder)
ss.setSpan(placeholderSpan, 0, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.setText(ss, TextView.BufferType.SPANNABLE) // 必需设置
val spannable = textView.text as? Spannable ?: return
Glide.with(textView)
.load(“https://sf6-ttcdn-tos.pstatp.com/img/user-avatar/d8111dfb52a63f3f12739194cf367754~100x100.png”)
.into(object : CustomTarget() {
override fun onResourceReady(resource: Drawable, transition: Transition?) {
val start = spannable.getSpanStart(placeholderSpan)
val end = spannable.getSpanEnd(placeholderSpan)
if (start != -1 && end != -1) {// 替换Span
ion: Transition?) {
val start = spannable.getSpanStart(placeholderSpan)
val end = spannable.getSpanEnd(placeholderSpan)
if (start != -1 && end != -1) {// 替换Span

上一篇:Hive 表类型简述


下一篇:[LeetCode] Palindrome Partitioning II 解题笔记