使用多行文本测量自定义视图的问题

时间:2012-03-21 05:14:31

标签: android text canvas

我正在尝试为列表项创建自定义视图,而不是使用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;
    }
}

1 个答案:

答案 0 :(得分:0)

好像你过度设计了这个。是否有理由需要自定义View?要做到这一点需要做很多工作,而且在你的案例中我没有看到任何特别的好处。除非你只是想学习如何编写自定义视图,我想。如果是这样的话。否则,我建议您使用RelativeLayout之类的内容,其中包含TextView个内容。它会使一切变得更加简单。

按原样,每次测量时都会创建一个新的TextView(糟糕但不可怕),每次绘制时都会创建一个新的TextView(非常糟糕)。一旦你克服了这个错误,你就会看到可怕的性能,可能包括GC捶打。无论如何,你正在制作TextView衡量标准,为什么不直接使用它呢?

如果你真的想要自己做自定义控件,我会缓存一个Paint对象,并使用breakTextFontMetrics的组合来衡量和绘制。