五种方法:
- drawableLeft、drawableTop。。(TextView中xml自带,通过drawPadding控制间距)
- 自定义TextView,重写TextView的Ondraw方法
BitmapFactory获取bitmap对象
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, getPaint());
super.onDraw(canvas);
}
缺点:不能灵活控制图片位置
- 使用html,安卓api支持
textView = (TextView) findViewById(R.id.tv);
String html = "<img src='" + R.drawable.ding + "'/>";
html += " <img src='" + R.drawable.jing + "' />";
ImageGetter imgGetter = new ImageGetter() {
@Override
public Drawable getDrawable(String source) {
// TODO Auto-generated method stub
int id = Integer.parseInt(source);
Drawable d = getResources().getDrawable(id);
d.setBounds(0, 0, d.getIntrinsicWidth() + 10, d.getIntrinsicHeight() + 10);
return d;
}
};
CharSequence charSequence = Html.fromHtml(html, imgGetter, null);
textView.setText(charSequence);
textView.append(" Html实现图文混排");
- imageSpan
textViewImageSpan= (TextView) findViewById(R.id.textViewImageSpan);
Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
//根据Bitmap对象创建ImageSpan对象
ImageSpan imageSpan=new ImageSpan(this,bitmap);
//创建一个SpinnableString对象,以便插入ImageSpan对象封装的图像
SpannableString spannableString=new SpannableString("replace");
//用ImageSpan对象替换replace字符串
spannableString.setSpan(imageSpan,0,7, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
//将图像显示在TextView上
textViewImageSpan.setText(spannableString);
- 扩展图片文字单行之间居中(间距字体大小都不影响)
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.tv);
SpannableString showString = new SpannableString(
"我 钟山风雨起苍黄百万雄师过大江虎踞龙盘今胜昔天翻地覆慨而慷宜将剩勇追穷寇不可沽名学霸王天若有情天亦老人间正道是沧桑。钟山风雨起苍黄百万雄师过大江虎踞龙盘今胜昔天翻地覆慨而慷宜将剩勇追穷寇不可沽名学霸王天若有情天亦老人间正道是沧桑。");
MyIm imageSpan = new MyIm(this, R.drawable.ding);
MyIm imageSpan2 = new MyIm(this, R.drawable.jing);
showString.setSpan(imageSpan, 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
showString.setSpan(imageSpan2, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(showString);
}
public class MyIm extends ImageSpan {
public MyIm(Context arg0, int arg1) {
super(arg0, arg1);
}
public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
Drawable d = getDrawable();
Rect rect = d.getBounds();
if (fm != null) {
FontMetricsInt fmPaint = paint.getFontMetricsInt();
// 获得文字、图片高度
int fontHeight = fmPaint.bottom - fmPaint.top;
int drHeight = rect.bottom - rect.top;
int top = drHeight / 2 - fontHeight / 4;
int bottom = drHeight / 2 + fontHeight / 4;
fm.ascent = -bottom;
fm.top = -bottom;
fm.bottom = top;
fm.descent = top;
}
return rect.right;
}
//getSize方法,返回一个Int含义为图片的宽度还设置了文字的ascent、descent的位置
@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 = getDrawable();
Paint.FontMetricsInt fm = paint.getFontMetricsInt();
int transY = (y + fm.descent + y + fm.ascent) / 2 - b.getBounds().bottom / 2;
canvas.save();
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
}
先解释一个类:Paint.FontMetrics,它表示绘制字体时的度量标准。google的官方api文档对它的字段说明如下:
ascent: 字体最上端到基线的距离
descent:字体最下端到基线的距离
回到主题,我们要让imagespan与text对齐,只需把imagespan放到descent线和ascent线之间的中间位置就可以了。
解释下形参:
x,要绘制的image的左边框到textview左边框的距离。
y,要替换的文字的基线坐标,即基线到textview上边框的距离。
top,替换行的最顶部位置。
bottom,替换行的最底部位置。注意,textview中两行之间的行间距是属于上一行的,所以这里bottom是指行间隔的底部位置。
paint,画笔,包含了要绘制字体的度量信息。
这几个参数含义在代码中找不到说明,写了个demo测出来的。top和bottom参数只是解释下,函数里面用不上。
然后解释下代码逻辑:
getDrawable获取要绘制的image,getBounds是获取包裹image的矩形框尺寸;
y + fm.descent得到字体的descent线坐标;
y + fm.ascent得到字体的ascent线坐标;
两者相加除以2就是两条线中线的坐标;
b.getBounds().bottom是image的高度(试想把image放到原点),除以2即高度一半;
前面得到的中线坐标减image高度的一半就是image顶部要绘制的目标位置;
最后把目标坐标传递给canvas.translate函数就可以了
显示效果: