Я пытаюсь создать собственное представление для своих элементов списка вместо использования макетов 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;
}
}