измерение проблемы с пользовательским представлением с многострочным текстом - PullRequest
0 голосов
/ 21 марта 2012

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

Похоже, вы чрезмерно проектируете это.Есть ли причина, по которой вам нужен кастом View?Это большая работа, чтобы получить права, и я не вижу особой выгоды в вашем случае.Если только вы не пытаетесь научиться писать собственные представления, я полагаю.Если дело обстоит именно так.В противном случае, я бы порекомендовал вам использовать что-то вроде RelativeLayout с несколькими TextView внутри.Это сделало бы все намного проще.

Как есть, вы создаете новый TextView каждый раз, когда вы измеряете (плохо, но не ужасно) и каждый раз, когда вы рисуете (очень плохо).Как только вы преодолеете эту ошибку, вы увидите ужасную производительность, возможно, включая GC Thrash.В любом случае вы делаете TextView измерение и рисование для вас, почему бы просто не использовать его напрямую?

Если вы действительно хотите сделать свой собственный элемент управления, я бы кешировал Paint объект и использовать комбинацию breakText и FontMetrics для измерения и рисования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...