【Android UI】圖片 + 文字展示by SpannableStringBuilder
阿新 • • 發佈:2019-01-22
起源
圖片和文字混合展示,比如這麼個需求,需要在每段文字的左邊要有一個小圓點,(小圓點符號在android系統中並不支援)。
先用TextView的setDrawableLeft,
嗯,達到要求,那麼換個行看看。
喔,糟糕,這個setDrawableLeft是為整個TextView服務的,而不只是為其中某一行準備的喲。
SpannableStringBuilder
為了滿足上面說到的需求,使用SpannableStringBuilder的setSpan方法可以將一串文字中的指定字元替換為圖片。我先繼承SpannableStringBuilder利用setSpan封裝出一個appendSpan方法出來。
public class MTSpannableStringBuilder extends SpannableStringBuilder {
public MTSpannableStringBuilder appendSpan(String text, Object what, int flags) {
int start = length();
append(text);
setSpan(what, start, length(), flags);
return this;
}
}
Drawable drawable = getResources().getDrawable (R.drawable.train_round);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
//要讓圖片替代指定的文字就要用ImageSpan
ImageSpan span = new ImageSpan(drawable,ImageSpan.ALIGN_BASELINE);
MTSpannableStringBuilder builder = new MTSpannableStringBuilder();
builder.appendSpan("ha",span, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView tt = (TextView) findViewById(R.id.spanableText);
builder.append("hahahahahahahhahahahahahahahahahahahahahahahahahaahhahahahahlasjljfljasdfasdfsdfadfasdfasdffsfsfsf");
tt.setText(builder);
最終的結果是:
哈哈,似乎快要接近了,但是圓點並不能垂直方向居中。希望能夠將圖片居中,需要經歷:
* 將圖片”拔高”到居中位置,
* 居中位置怎麼計算
如下程式碼中onDraw先將圖片和文字內容的bottom對齊,然後再將圖片和文字內容居中對齊,對齊的方式就是通過減去[(fontAscent-fontDecent) - drawableHeight]/2。
public class CenteredImageSpan extends ImageSpan {
private WeakReference<Drawable> mDrawableRef;
public CenteredImageSpan(Context context, final int drawableRes) {
super(context, drawableRes);
}
@Override
public int getSize(Paint paint, CharSequence text,
int start, int end,
Paint.FontMetricsInt fm) {
Drawable d = getCachedDrawable();
Rect rect = d.getBounds();
if (fm != null) {
Paint.FontMetricsInt pfm = paint.getFontMetricsInt();
// keep it the same as paint's fm
fm.ascent = pfm.ascent;
fm.descent = pfm.descent;
fm.top = pfm.top;
fm.bottom = pfm.bottom;
}
return rect.right;
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text,
int start, int end, float x,
int top, int y, int bottom, @NonNull Paint paint) {
Drawable b = getCachedDrawable();
canvas.save();
int drawableHeight = b.getIntrinsicHeight();
int fontAscent = paint.getFontMetricsInt().ascent;
int fontDescent = paint.getFontMetricsInt().descent;
int transY = bottom - b.getBounds().bottom + // align bottom to bottom
(drawableHeight - fontDescent + fontAscent) / 2; // align center to center
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
// Redefined locally because it is a private member from DynamicDrawableSpan
private Drawable getCachedDrawable() {
WeakReference<Drawable> wr = mDrawableRef;
Drawable d = null;
if (wr != null) {
d = wr.get();
}
if (d == null) {
d = getDrawable();
mDrawableRef = new WeakReference<>(d);
}
return d;
}
}
然後得到如下令人滿意的結果: