onMeasure() задает размеры view и является важной частью контракта между view и layout.
onMeasure()
вызывается после вызова метода
measure(). Обычно это делает layout для всех дочерних view, чтобы определить их размеры.
В некоторых случаях при реализации кастомной view требуется переопределить этот метод.
Для правильной реализации метода
onMeasure()
нужно сделать следующее:
1.
onMeasure()
вызывается с аргументами
widthMeasureSpec
и
heightMeasureSpec
. Это целочисленные значения в которых закодирована информация о предпочтениях layout к размерам view. На этом шаге вам нужно декодировать measure spec и получить значение размера и режим, определяющий как этот размер применять.
В следующем посте разберем measure spec подробнее.
2. Вычислить ширину и высоту view. При вычислении размеров необходимо учитывать значения паддингов и measure spec. Если вычисленный размер превышает measure spec, то layout выбирает что делать. Layout может обрезать view, добавить скроллинг, бросить исключение или вызвать
onMeasure()
еще раз с новыми значениями measure specs.
3. После вычисления ширины и высоты необходимо вызвать метод
setMeasuredDimension(width: Int, height: Int). Если не вызвать этот метод, то будет брошен
IllegalStateException
.
Для правильной реализации метода
onMeasure()
необходимо учитывать значения параметров
widthMeasureSpec
и
heightMeasureSpec
, с которыми вызван
onMeasure()
.
Эти параметры имеют тип
int
, и представляют собой целые числа, в которых с помощью
битового сдвига закодировано два значения: размер в пикселях и режим (mode) применения этого размера. Для значений measure specs используется тип
int
, а не специальный класс, чтобы сэкономить память, используемую при отрисовки UI.
Для получения значений размера и режима используются статические методы
MeasureSpec.getSize(measureSpec: Int) и
MeasureSpec.getMode(measureSpec: Int) соответственно.
Режим может иметь одно из трех значений:
UNSPECIFIED – у родителя нет предпочтений к размеру view, размер может быть произвольным. Иногда это значение используется лэйаутом при первом проходе для определения желаемых размеров каждой из view. После чего measure() вызывается еще раз, но уже с другим режимом.
EXACTLY – родитель определил и передал точный размер view. View будет иметь этот размер независимо от того, какого размера view хочет быть.
AT_MOST – родитель определил и передал верхнюю границу размера view. View может быть любого размера в пределах этой границы.
На втором шаге, описанном
в первом посте, нужно определить желаемые размеры view. Тут нет универсального решения. Размер зависит от целей view и от того, что нужно отобразить. При определении размера не забывайте учитывать заданные паддинги. Паддинги получают методами
getPadding...()
.
После определения желаемого размера нужно сопоставить его с требуемыми
measure specs. Для этого удобно использовать статический метод
resolveSize(size: Int, measureSpec: Int).
resolveSize()
принимает параметрами желаемый размер и measure spec и возвращает новое значение размера. Если значение measuare spec
EXACTLY
, то
resolveSize()
возвращает размер, переданный в measure spec. Если
AT_MOST
, то возвращается минимальное значение из желаемого размера и размера measure spec. Если
UNSPECIFIED
, то
resolveSize()
возвращает желаемый размер.
На скриншоте реализация
onMeasure()
с использованием
resolveSize()
.
calculateHeight()
и
calculateWidth()
– это ваши методы, которые считают желаемые высоту и ширину.
Статья с более подробной информацией и примерами реализации
onMeasure()
.