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.
Для правильной реализации метода onMeasure() необходимо учитывать значения параметров widthMeasureSpec и heightMeasureSpec, с которыми вызван onMeasure(). Эти параметры имеют тип int, и представляют собой целые числа, в которых с помощью битового сдвига закодировано два значения: размер в пикселях и режим (mode) применения этого размера. Для значений measure specs используется тип int, а не специальный класс, чтобы сэкономить память, используемую при отрисовки UI.
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().