Лучшие практики для работы с дорогостоящим вычислением высоты вида?

Я постоянно сталкиваюсь с проблемой размера и макета для настраиваемых представлений, и мне интересно, может ли кто-нибудь предложить подход «передовой практики». Проблема в следующем. Представьте себе настраиваемое представление, в котором высота, необходимая для содержимого, зависит от ширины представления (аналогично многострочному TextView). (Очевидно, это применимо только в том случае, если высота не зафиксирована параметрами макета.) Загвоздка в том, что для заданной ширины довольно дорого вычислять высоту содержимого в этих настраиваемых представлениях. В частности, вычисление в потоке пользовательского интерфейса слишком дорого, поэтому в какой-то момент рабочий поток должен быть запущен для вычисления макета, а когда он будет завершен, пользовательский интерфейс необходимо обновить.

Вопрос в том, как это должно быть спроектировано? Я придумал несколько стратегий. Все они предполагают, что всякий раз, когда вычисляется высота, соответствующая ширина записывается.

Первая стратегия показана в этом коде:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = measureWidth(widthMeasureSpec);
    setMeasuredDimension(width, measureHeight(heightMeasureSpec, width));
}

private int measureWidth(int widthMeasureSpec) {
    // irrelevant to this problem
}

private int measureHeight(int heightMeasureSpec, int width) {
    int result;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);
    if (specMode == MeasureSpec.EXACTLY) {
        result = specSize;
    } else {
        if (width != mLastWidth) {
            interruptAnyExistingLayoutThread();
            mLastWidth = width;
            mLayoutHeight = DEFAULT_HEIGHT;
            startNewLayoutThread();
        }
        result = mLayoutHeight;
        if (specMode == MeasureSpec.AT_MOST && result > specSize) {
            result = specSize;
        }
    }
    return result;
}

Когда поток макета завершается, он отправляет Runnable потоку пользовательского интерфейса для установки mLayoutHeight на вычисленную высоту, а затем вызовите requestLayout () invalidate () ).

Вторая стратегия - всегда иметь onMeasure используйте текущее значение для mLayoutHeight (без запуска потока макета). Проверка изменений ширины и запуск потока макета будет выполняться путем переопределения onSizeChanged .

Третья стратегия - лениться и ждать запуска потока макета (при необходимости) в onDraw .

Я хотел бы минимизировать количество запусков и / или прерываний потока макета, а также как можно скорее вычислить требуемую высоту. Вероятно, было бы хорошо минимизировать количество вызовов requestLayout () .

Из документации ясно, что onMeasure может вызываться несколько раз в течение единый макет. Менее ясно (но кажется вероятным), что onSizeChanged также может вызываться несколько раз. Поэтому я думаю, что размещение логики в onDraw могло бы быть лучшей стратегией. Но это кажется противоречащим духу настраиваемого размера представления, поэтому у меня, по общему признанию, иррациональное предубеждение против этого.

Другие люди, должно быть, сталкивались с той же проблемой. Есть ли подходы, которые я пропустил? Есть ли лучший подход?

16
задан Ted Hopp 10 August 2011 в 17:24
поделиться