evernote中的笔记,这里分享一下:
用textview可以显示html富文本,比显示单纯的文字要友好很多,效果图:
但是问题也很多。
首先是 html 的支持实在有限,很多在html中比较easy的事情,在这里就感觉很困难,项目中遇到的两个问题:
1. 调整字体大小的时候,图片不随着变化,显得不协调
2. 调整textview行间距的时候,文字间间距是变大了,并且文字居上,但是图片居下,导致文字和图片错开。
由于无法通过html的方式来解决,只能去看android是否提供了相关方法了,结果并没有方法能够直接支持这样的需要,只好想办法了。
问题1解决:
观察方法时发现:
Html.fromHtml(html, new ImageGetter() {...}, null);
这里有个ImageGetter,这里有个:
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
大概意思是按照drawable的宽高来设置边界,考虑调整这里,将边界*2会是神马效果。运行之后发现,图标变大了一倍,那么问题1就有办法了,只要将这里的图片大小和字体大小对应上就可以,在字体大小调整的时候,通知到这里就行。 这里的图片是根据字体大小16sp来做的,所以得到当前字体大小,按比例放大图标应该就可以。
drawable.setBounds(0, 0,
(int)(drawable.getIntrinsicWidth()*(cFontSize/16.0)),
(int)(drawable.getIntrinsicHeight()*(cFontSize/16.0)));
最后代码为:
Spanned spanned = Html.fromHtml(content, new ImageGetter(){
@Override
public Drawable getDrawable(String source) {
Drawable drawable = getResources().getDrawable(
getResourceId(source));
drawable.setBounds(0, 0,
(int)(drawable.getIntrinsicWidth()*(cFontSize/16.0)),
(int)(drawable.getIntrinsicHeight()*(cFontSize/16.0)));
return drawable;
}
}, null);
问题2解决:
这里解决的时候,参考了网上的资料,
http://stackoverflow.com/questions/3253148/imagespan-is-cut-off-incorrectly-aligned
主要是重写了ImageSpan,因为ImageSpan只有两个:ImageSpan.ALIGN_BASELINE 和 ImageSpan.ALIGN_BOTTOM ,这两个都不能满足和文字平行的需要。
首先是普通处理的代码。
Spanned spanned = Html.fromHtml(content, new ImageGetter() {...} , null);
if (spanned instanceof SpannableStringBuilder) {
ImageSpan[] imageSpans = spanned.getSpans(0, spanned.length(), ImageSpan.class);
for (ImageSpan imageSpan : imageSpans) {
int start = spanned.getSpanStart(imageSpan);
int end = spanned.getSpanEnd(imageSpan);
Drawable d = imageSpan.getDrawable();
ImageSpan newImageSpan = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);
((SpannableStringBuilder) spanned).setSpan(newImageSpan, start, end, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
((SpannableStringBuilder) spanned).removeSpan(imageSpan);
}
}
这里从html中得到所有的ImageSpan,并设置好对应的属性,将新的span加入进去,并移除旧的。思路就在将新的span换成符合自己要求的span。
@Override
public void draw(Canvas canvas, CharSequence text,
int start, int end, float x,
int top, int y, int bottom, Paint paint) {
Drawable b = mDrawable;
canvas.save();
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
int textLength = text.length();
for (int i = 0; i < textLength; i++) {
if (Character.isLetterOrDigit(text.charAt(i))) {
transY -= paint.getFontMetricsInt().descent;
break;
}
}
}
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
说实话,这里面都干得啥,老外没写注释,图片处理也不熟,只能自己来试了。translate是偏移的意思,就拿他开刀,给transY一个值,运行之后发现,图片真的Y方向偏移了,问题2也就有办法了。得到textView设置的行间距,从dip转换为px,用transY减掉,那么图片就会相应的向上偏移行间距的长度,也就和文字平行了。调整之后的类:
public class StickerSpan extends ImageSpan {
public StickerSpan(Drawable b, int verticalAlignment) {
super(b, verticalAlignment);
}
@Override
public void draw(Canvas canvas, CharSequence text,
int start, int end, float x,
int top, int y, int bottom, Paint paint) {
Drawable b = getDrawable();
canvas.save();
int transY = bottom - b.getBounds().bottom - Utils.dip2px(WApplication.cFontLineSpacingExtra);
if (mVerticalAlignment == ALIGN_BASELINE) {
int textLength = text.length();
for (int i = 0; i < textLength; i++) {
if (Character.isLetterOrDigit(text.charAt(i))) {
transY -= paint.getFontMetricsInt().descent;
break;
}
}
}
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
}
然后将 ImageSpan newImageSpan = new ImageSpan(...);
修改为 StickerSpan StickerSpan = new StickerSpan(…);
对比一下最后的效果图: