«

»

Фев
19

Ограничение размеров UI компонентов в Андроид. Overriding OnMeasure

Когда прямоугольное хочется сделать квадратным…

Андроид предлагает разнообразные средства управления размерами компонентов UI. Вы можете задать минимальную/максимальную ширину или высоту, можете сделать так, чтобы вид занимал все свободное пространство или был достаточного размера, чтобы отобразить все свое содержимое. Но иногда встроенной логики не хватает. В данной статье на примере ImageButton будет рассказано, как скорректировать размеры вида следуя собственным правилам.

Введение

Для определенности будем рассматривать конкретное задание: кнопки ImageButton должны быть определенной ширины (определяемой изображением), а высота их должна быть больше либо равна ширине. Пример как должно и не должно быть приведен на рисунках ниже.

правильно неправильно

Как обычно, есть несколько способов добиться такого поведения, некоторые из которых приведены ниже:

  • можно задать кнопке свойство minHeight. Тогда высота кнопки будет не меньше заданной. Но сразу возникают проблемы: нужно заранее знать какой ширины изображение мы будем помещать на кнопку. Но это еще не самое худшее. Свойство minHeight задает общую высоту компонента без учета отступов, которые задаются для нашей кнопки с помощью NinePatch изображения. Следовательно, поменяли отступы – идем вручную рассчитывать и задавать значение minHeight, что совсем неудобно.
  • можно перед тем как поместить изображение на кнопку узнать его ширину и высоту, а потом с помощью метода setLayoutParams, задать правильный размер кнопке, не забыв при этом учесть отступы. Нам потребуется написать существенное количество кода и не забывать его каждый раз вызывать. Плюс получаем сложности с созданием наших кнопок с помощью xml, так как недостаточно просто описать нашу кнопку на xml, нужно еще не забыть вызвать код для расчета правильных размеров. Сложно и запутанно.
  • можно создать новый класс для кнопок и переопределить метод onMeasure, таким образом, чтобы он при рассчитывал ширину и высоту кнопки согласно нашим требованиям. Все будет работать автоматически и не повлечет за собой дополнительных накладных расходов. Кажется, это лучшее, что можно сделать.

Переопределяем onMeasure

Из документации узнаем, что метод onMeasure должен рассчитать размеры вида и передавать рассчитанные значения в метод setMeasuredDimension, тогда Андроид будет знать сколько места на экране отвести под конкретный вид. Узнать рассчитанные высоту и ширину вида можно с помощью функций getMeasuredHeight и getMeasuredWidth. Класс ImageButton уже имеет стандартную реализацию метода onMeasure, работа которого нас почти устраивает, за исключением того, что нам требуется, чтобы высота кнопки была не меньше ее ширины. Следовательно напрашивается следующий план действий:

  • создаем новый класс производный от ImageButton
  • переопределяем метод onMeasure
  • вызываем стандартный метод onMeasure класса ImageButton
  • с помощью функций getMeasuredHeight и getMeasuredWidth получаем рассчитанные значения ширины и высоты
  • корректируем рассчитанную высоту и ширину кнопки, следуя нашей логике
  • задаем новые размеры кнопки с помощью метода setMeasuredDimension

Приступаем к реализации

Создаем новый класс, назовем его SquareImageButton. Генерируем для нашего класса такие же конструкторы, как и у класса ImageButton, тогда мы сможем использовать новый класс так же, как будет это стандартный ImageButton.

Созданный нами класс пока функционирует точно так же как и ImageButton. Приступаем к добавлению нового функционала. Для этого переопределяем метод onMeasure и реализуем новую логику его работы.

Новый метод onMeasure работает как же как и стандартный метод, за исключением того, что если рассчитанная высота кнопки меньше ее ширины, высота принимается равной ширине. Логика использования нового класса SquareImageButton в приложениях ничем не отличается от класса ImageButton, что очень удобно. Нужно лишь заменить ImageButton на SquareImageButton и все.

Смотрим, что получилось

Как было замечено, использование класса SquareImageButton не отличается от класса ImageButton, стоит лишь поменять имя, остальной код будет работать так же хорошо как и раньше. Поэтому приведу лишь один пример создания новых кнопок с помощью xml разметки.

Получаем следующий результат:

стандартный ImageButton SquareImageButton

Созданный класс работает в точности, как и ожидалось. Высота кнопки рассчитывается с учетом ее ширины. Никакого дополнительного кода или заметного ухудшения производительности в предложенной реализации нет. Сохранена совместимость с классом ImageButton. В приложении нужно лишь поменять имя класса кнопок, чтобы добиться их правильного размера, весь остальной код останется работоспособным.

Если статья оказалась вам полезной или почему на сайте есть реклама

4 комментариев

  1. Михаил Сообщает:

    Спасибо. Очень полезная статья.
    Есть ли какие-то нюансы при переопределении метода onMeasure для различных контейнеров? Например для ViewGroup. Мне необходимо динамически добавлять на компонент кастомные View разных размеров. Покопал, понял, что есть обязательный метод onLayout, который перерисовывает контейнер после добавления новых компонентов. но взаимосвязь мне их не понятна.

  2. Iens Сообщает:

    Спасибо! Очень доступно изложено!

  3. Svyatozar Сообщает:

    Спасибо большое, очень помогло

  4. Yan Сообщает:

    Просто и доступно,спасибо, очень хорошо объясняете=) пишите исчО

Добавить комментарий

Мы сохраним Ваш e-mail в тайне.