我正在尝试为列表项创建自定义视图,而不是使用XML布局。视图只是我正在绘制到画布上的文本集合。除了固定大小的标题之外,还有一个多行的消息体,应该调整大小以适应列表项的宽度(减去填充)。
问题是某些项目视图太小或太大,因此会有大量空白区域或文本被切断。我认为问题在于测量onMeasure中的多行文本。
这就是视图的样子
/----------------\
|username time|
+----------------+
| multi-line |
| message body |
\----------------/
这是视图的来源。
class MsgListItem extends View
{
private final Paint paint = new Paint();
private Bundle data;
private String username;
private int gametype;
private int index;
private Context cntx;
final static class Cache
{
public static Typeface fontNormal;
public static Typeface fontItalic;
public static String username;
public static int dpi;
public static void Init(final Context context, final String Username)
{
fontNormal = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Regular.ttf");
fontItalic = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Italic.ttf");
username = Username;
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
dpi = (int) ((1 + Math.max(metrics.ydpi, metrics.xdpi)) / 160);
}
}
public MsgListItem(final Context context)
{
super(context);
cntx = context;
paint.setAntiAlias(true);
paint.setTypeface(Cache.fontNormal);
paint.setTextSize(20 * Cache.dpi);
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
{
final int width = MeasureSpec.getSize(widthMeasureSpec);
// BUG: height is either not enough or too much
final int height = getTextHeight(data.getString("msg"), width - 32);
setMeasuredDimension(width, height + 45 * Cache.dpi);
}
@Override
protected void onDraw(final Canvas canvas)
{
final int width = getMeasuredWidth();
// draw msg header
if (data.getString("username").equals(Cache.username))
paint.setColor(0xffd2d0ff);
else
paint.setColor(0xffcce6ff);
canvas.drawRect(0, 0, getWidth(), 30 * Cache.dpi, paint);
paint.setColor(0xff000000);
canvas.drawText(data.getString("username"), 8 * Cache.dpi, 22 * Cache.dpi, paint);
paint.setTextAlign(Paint.Align.RIGHT);
paint.setTypeface(Cache.fontItalic);
final String time = new PrettyDate(data.getString("time")).agoFormat();
canvas.drawText(time, width - 8 * Cache.dpi, 22 * Cache.dpi, paint);
paint.setTextAlign(Paint.Align.LEFT);
paint.setTypeface(Cache.fontNormal);
// draw msg body
final Bitmap tbit = getTextBitmap(data.getString("msg"), width - 32);
canvas.drawBitmap(tbit, 16, 30 * Cache.dpi, paint);
}
private Bitmap getTextBitmap(final String str, final int width)
{
final TextView tv = new TextView(cntx);
tv.setDrawingCacheEnabled(true);
tv.setText(str);
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, 22 * Cache.dpi);
tv.measure(width | MeasureSpec.EXACTLY, MeasureSpec.UNSPECIFIED);
tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
return tv.getDrawingCache();
}
private int getTextHeight(final String str, final int width)
{
final TextView tv = new TextView(cntx);
tv.setText(str);
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, 22 * Cache.dpi);
tv.measure(width | MeasureSpec.EXACTLY, MeasureSpec.UNSPECIFIED);
return tv.getMeasuredHeight();
}
public void setData(final Bundle bundle, final int Index)
{
data = bundle;
index = Index;
}
}
答案 0 :(得分:0)
好像你过度设计了这个。是否有理由需要自定义View
?要做到这一点需要做很多工作,而且在你的案例中我没有看到任何特别的好处。除非你只是想学习如何编写自定义视图,我想。如果是这样的话。否则,我建议您使用RelativeLayout
之类的内容,其中包含TextView
个内容。它会使一切变得更加简单。
按原样,每次测量时都会创建一个新的TextView(糟糕但不可怕),每次绘制时都会创建一个新的TextView(非常糟糕)。一旦你克服了这个错误,你就会看到可怕的性能,可能包括GC捶打。无论如何,你正在制作TextView
衡量标准,为什么不直接使用它呢?
如果你真的想要自己做自定义控件,我会缓存一个Paint
对象,并使用breakText
和FontMetrics
的组合来衡量和绘制。