}
这里对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
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