Я постоянно сталкиваюсь с проблемой размера и макета для настраиваемых представлений, и мне интересно, может ли кто-нибудь предложить подход «передовой практики». Проблема в следующем. Представьте себе настраиваемое представление, в котором высота, необходимая для содержимого, зависит от ширины представления (аналогично многострочному 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
могло бы быть лучшей стратегией. Но это кажется противоречащим духу настраиваемого размера представления, поэтому у меня, по общему признанию, иррациональное предубеждение против этого.
Другие люди, должно быть, сталкивались с той же проблемой. Есть ли подходы, которые я пропустил? Есть ли лучший подход?