在阅读了你提供的优秀文章、查看了Android源代码并编写了大量
Log.d()
之后,我终于明白你需要的是——准备好了吗?——一个
ReplacementSpan
子类。
对于你的情况来说,
ReplacementSpan
是有些违反直觉的,因为你并没有替换文本,而是在绘制一些额外的东西。但事实证明,
ReplacementSpan
正是你需要的两个东西:用于为你的图形调整行高的钩子和用于绘制你的图形的钩子。所以你只需在其中绘制文本,因为超类不会这样做。
我一直对spans和文本布局感兴趣,所以我开始了一个演示项目进行尝试。
我为你想出了两种不同的方法。在第一个类中,你有一个可以作为
Drawable
访问的图标。你将
Drawable
传递到构造函数中。然后你使用
Drawable
的尺寸来帮助调整行高。这里的好处是
Drawable
的尺寸已经针对设备的显示密度进行了调整。
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.style.ReplacementSpan;
import android.util.Log;
public class IconOverSpan extends ReplacementSpan {
private static final String TAG = "IconOverSpan";
private Drawable mIcon;
public IconOverSpan(Drawable icon) {
mIcon = icon;
Log.d(TAG, "<ctor>, icon intrinsic dimensions: " + icon.getIntrinsicWidth() + " x " + icon.getIntrinsicHeight());
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
if (fm != null) {
int h = mIcon.getIntrinsicHeight();
if (- fm.top + fm.ascent < h) {
fm.top = fm.ascent - h;
}
}
int textWidth = (int) Math.ceil(paint.measureText(text, start, end));
int w = mIcon.getIntrinsicWidth();
Log.d(TAG, "getSize(), returning " + textWidth + ", fm = " + fm);
return Math.max(textWidth, w);
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
Log.d(TAG, "draw(), x = " + x + ", top = " + top + ", y = " + y + ", bottom = " + bottom);
canvas.drawText(text, start, end, x, y, paint);
mIcon.setBounds((int) x, top, (int) x + mIcon.getIntrinsicWidth(), top + mIcon.getIntrinsicHeight());
mIcon.draw(canvas);
}
}
第二个想法更好,如果您打算使用非常简单的形状来制作图形。您可以为形状定义一个
Path
,然后只需渲染
Path
。现在您必须考虑显示密度,为了使它更容易,我只是从构造函数参数中获取它。
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.text.style.ReplacementSpan;
import android.util.Log;
public class PathOverSpan extends ReplacementSpan {
private static final String TAG = "PathOverSpan";
private float mDensity;
private Path mPath;
private int mWidth;
private int mHeight;
private Paint mPaint;
public PathOverSpan(float density) {
mDensity = density;
mPath = new Path();
mWidth = (int) Math.ceil(16 * mDensity);
mHeight = (int) Math.ceil(16 * mDensity);
mPath.moveTo(mWidth/2, 0);
mPath.lineTo(mWidth, mHeight);
mPath.lineTo(0, mHeight);
mPath.close();
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL);
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
if (fm != null) {
if (- fm.top + fm.ascent < mHeight) {
fm.top = fm.ascent - mHeight;
}
}
int textWidth = (int) Math.ceil(paint.measureText(text, start, end));
return Math.max(textWidth, mWidth);
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
Log.d(TAG, "draw(), x = " + x + ", top = " + top + ", y = " + y + ", bottom = " + bottom);
canvas.drawText(text, start, end, x, y, paint);
int textWidth = (int) Math.ceil(paint.measureText(text, start, end));
int offset = 0;
if (textWidth > mWidth) {
offset = (textWidth - mWidth) / 2;
}
canvas.translate(x + offset, top);
canvas.drawPath(mPath, mPaint);
canvas.translate(-x - offset, -top);
}
}
以下是我在主活动中如何使用这些类:
SpannableString spannableString = new SpannableString("Some text and it can have an icon over it");
UnderlineSpan underlineSpan = new UnderlineSpan();
IconOverSpan iconOverSpan = new IconOverSpan(getResources().getDrawable(R.drawable.ic_star));
PathOverSpan pathOverSpan = new PathOverSpan(getResources().getDisplayMetrics().density);
spannableString.setSpan(underlineSpan, 5, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(iconOverSpan, 21, 25, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(pathOverSpan, 29, 38, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView textView = (TextView) findViewById(R.id.textView);
textView.setText(spannableString);
好的!现在我们都学到了一些东西。
android.text.style.ReplacementSpan
。 - pskink