好累的两周。
经历了上海的房子被二房东收回去,自己说没得住就没得住,经历了辞职,经历了苏州找房,现在还在上海短租着上着班,漂泊不定,整个人会很沮丧,想想真的很难受。这个时候感觉自己应该干点什么事情,比如把之前想总结的 ImageSpan 的用法给写了。
ImageSpan 是图文混排中最重要的一部分,TextView 之所以能做到图文混排也就是因为 ImageSpan 的存在。
首先看一下最简单的本地图片的加载。
1 2 3 4 5 6 7 8
| TextView textView = findViewById(R.id.image_span);
SpannableString spannableString = new SpannableString("0000000ddddddddd"); Drawable drawable = ContextCompat.getDrawable(this, R.drawable.ic_launcher_foreground); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); spannableString.setSpan(new ImageSpan(drawable), 4, 5, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setText(spannableString);
|
当然我们可以通过拼接的方式,多个 SpannableString 组合成为一个 SpannableBuilder 来加载多张图片,但是值得注意的是, drawable 必须要 setBounds,Drawable的setBounds方法有四个参数,setBounds(int left, int top, int right, int bottom),这个四参数指的是drawable将在被绘制在canvas的哪个矩形区域内。这里的单位是 px 不是 dp。建议自己写一个 DisplayUtils 或者使用 Kotlin 提供的 anko 库等方式来转换。
但是我们使用本地图片的时候终究少,更多时候我们会使用网络图片,如何加载网络图片就成了一个新的问题,我在开发的时候选择的方案是第三方图片加载框架的配合使用。这里要注意的是,加载图片应该是在子线程完成,然后主线程刷新的。
首先我们需要创建一个 BitmapDrawable 的对象
1 2 3 4 5 6 7 8 9
| class URLDrawable : BitmapDrawable() { var drawable: Drawable? = null
override fun draw(canvas: Canvas) { if (drawable != null) { drawable!!.draw(canvas) } } }
|
然后我们就要开始写这个 AsyncTask 了。这里提一下 AsyncTask,它是 Android 提供的一个助手类,它对 Thread 和 Handler 进行了封装,方便我们使用。Android 之所以提供 AsyncTask 这个类,就是为了方便我们在后台线程中执行操作,然后将结果发送给主线程,从而在主线程中进行UI更新等操作。
直接放代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class ImageGetterAsyncTask(private val context: Context, private val urlDrawable: URLDrawable, val url: String) : AsyncTask<TextView, Void, Bitmap?>() { private var textView: TextView? = null
override fun doInBackground(vararg params: TextView): Bitmap? { textView = params[0] return try { Picasso.get().load(url).get() } catch (e: Exception) { null } }
override fun onPostExecute(bitmap: Bitmap?) { val bitmapDrawable = BitmapDrawable(context.resources, bitmap) bitmapDrawable.setBounds(0, 0, 100, 100) urlDrawable.setBounds(0, 0, 100, 100) urlDrawable.drawable = bitmapDrawable urlDrawable.invalidateSelf() textView!!.invalidate() } }
|
这里一定要把 textview 传进来,因为这样我们才能实现刷新 UI 的目的。
我选用的是 Picasso 来加载图片,当然还可以选择其他的框架,只要稍作修改就可以了。
最后是 URLImageParser
1 2 3 4 5 6 7 8
| class URLImageParser(private val textView: TextView, private val context: Context) {
fun getDrawable(url: String): Drawable { val urlDrawable = URLDrawable() ImageGetterAsyncTask(context, urlDrawable, url).execute(textView) return urlDrawable } }
|
至于使用,则很简单,因为我们已经拿到了一个 drawable 的对象了。
1 2 3
| val drawableFromNet = URLImageParser(textView, this).getDrawable("https://www.baidu.com/img/bd_logo1.png") drawableFromNet.setBounds(0, 0, 100, 100) spannableString.setSpan(ImageSpan(drawableFromNet), 4, 5, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
最后还有一种是特殊的情况,就是加载 database 64 图片。
1 2 3 4 5 6
| val stream = ByteArrayInputStream(Base64.decode(imageDataBytes.toByteArray(), Base64.DEFAULT)) val bitmap = BitmapFactory.decodeStream(stream) val bitmapDrawable = BitmapDrawable(env.resources, bitmap) bitmapDrawable.setBounds(0, 0, widthInt, heightInt) spannableString.setSpan(TransYImageSpan(bitmapDrawable, env.px2dip(exVerticalAlign.toInt())), 0, spannableString.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) builder.append(spannableString)
|
代码已上传 Github ImageSpan