«

»

Мар
24

Разрабатываем custom Like/Unlike view для Андроид.

Часто стандартных элементов GUI Android SDK не хватает, и возникают ситуации, когда необходимо создать новый целостный элемент интерфейса, реализующий определенную функцию. В данной статье я хочу поделиться опытом создания собственного контрола (custom view) для Андроид приложения. Данный контрол реализует функцию нравится/не нравится (Like/Unlike он же Лайк/Не лайк), его устройство и дизайн досталось мне от дизайнера и было сказано: “Чтобы в приложении выглядел, как в Photoshop”. Сказано – сделано.

Открываем Photoshop и в очередной раз ругаем про себя дизайнера и думаем: “Когда же они @!!kjlk@! научатся правильно рисовать?” Но при более подробной рассмотрении все оказывается не так уж и плохо.

Нам требуется реализовать:

  • кнопка Лайк – зеленый круг с направленным вверх большим пальцем
  • кнопка Не лайк – красный круг с направленным вниз большим пальцем
  • зеленую и красную полоску, внутри которой отображается количество лайков/не лайков (счетчики). Причем чем больше лайков, тем длиннее зеленая полоска и короче красная, и соответственно наоборот.

Планируем реализацию.

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

  • Кнопки. Для реализации кнопок состоящих из изображения есть стандартный компонент ImageButton. На понадобится их 2.
  • Счетчики. На счетчиках у нас отображается текст, следовательно берем TextView. Зададим им фон в виде градиента. Осталось придумать, как сделать, чтобы они растягивались в зависимости от показателей… вспоминаем, что мы можем задать вес вида, чтобы он занимал свободное пространство в определенной пропорции с другими видами в этом же контейнере. Чтобы использовать вес, поместим наши TextView в LineaLayout.
  • Контейнер. Теперь надо придумать, где бы разместить наши кнопки и счетчики. Счетчики должны быть позади кнопок. Для таких целей подойдет FrameLayout или RelativeLayout. Берем FrameLayout – он полегче и нам его должно хватить для реализации задуманного.
  • Графика. Вытаскиваем из фотошопа картинки кнопок (нажатую/не нажатую), вырезаем полоску градиента для счетчиков, узнаем цвета шрифтов.

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

Начинаем кодить. Первое, что делаем – создаем новый класс нашего контрола, назовем его LikeUnLikeControl. Так за основу мы взяли FrameLayout, поэтому наследуемся от него. Добавляем конструкторы, такие же как у родителя. Создаем приватную функцию initControl, которая будет настраивать наш контрол и добавляем ее вызов в конструкторы. Должно получится следующее.

Сейчас наш контрол ничем не отличается от стандартного FrameLayout. Приступаем к созданию кнопок и счетчиков. Готовим графический материал (про это я рассказывать не будут). Нам понадобиться изображение кнопки лайк и не лайк в нажатом и обычном состоянии. И градиент со счетчиков. Градиент не простой – программно его реализовать тяжело, поэтому вырезаем вертикальную полоску шириной в 1 пиксель из красного и зеленого градиента. Теперь нужно создать разметку.

Создаем разметку.

Создадим файлик в папке layout с названием like_unlike_control.xml, в котором зададим положение и дизайн наших элементов – содержимого LikeUnLikeControl. Свойства элементов, когда их много, я люблю писать в стилях, чтобы не захламлять разметку, поэтому в разметке будут ссылки на стили. Смотрим результат.

Комментарии:

  • тег merge выступает корневым тегом в нашей разметке. Любая разметка, которую вы создадите в xml формате должна начинаться с корневого элемента. Так как в нашей разметке нет корневого элемента (FrameLayout мы создаем программно), то в качестве корневого элемента используем тег merge.
  • LinearLayout – контейнер для наших счетчиков. Так как в этом файле разметки мы описываем элементы, которые будут помещены в FrameLayout, то сначала нужно описать виды, которые будут на заднем плане, так как каждый следующий вид в разметке будет накрывать предыдущий. Счетчики располагаются в нижней части нашего контрола, поэтому задаем свойство layout_gravity равным bottom.
  • TextView – счетчики. Их два. Располагаем их внутри LinearLayout.
  • ImageButton – кнопки лайк/не лайк. Их тоже 2. Чтобы кнопка лайк была слева, а не лайк была права задаем соответствующие значение свойству layout_gravity кнопок.

Пишем стили.

Приступаем к стилизации. Вы уже наверняка заметили в разметке ссылки на стили. Как и в предыдущий раз смотрим, что получилось, а потом анализируем. Создаем файлик like_unlike_control.xml в папке values. В нем будет хранить все стили для нашего контрола.

Комментарии:

  • like_unlike_imagebutton – стиль для кнопок. Указываем, что размер кнопки будет таким же как и размер изображения внутри кнопки (wrap_content). Задаем цвет фона прозрачным, так как по-умолчанию фон серый градиент с рамкой (как в стандартной кнопке)
  • like_unlike_rating – общие свойства для счетчиков. Задаем нужную высоту счетчика (layout_height), ширина счетчика рассчитывается в зависимости от его содержимого (layout_width). Задаем вес по-умолчанию равный 1 (layout_weight, об этом свойстве будет рассказано подробнее в дальнейшем). Так же задаем размер и стиль шрифта, которым будем выводить значение счетчика (textSize, textStyle). Указываем, что счетчик всегда надо отображать в одну строку и выводить целиком (ellipsize, maxLines).
  • like_unlike_rating.like/unlike. Так как некоторые свойства у счетчиков отличаются (цвет шрифта и др.), то для каждого счетчика необходимо создать соответствующий стиль, который мы унаследуем от стиля like_unlike_rating, в котором содержатся значения свойств для счетчиков. Эти стили определяют цвет шрифта (textColor), цвет фона счетчиков (background), задают правильное выравнивание текста внутри счетчика (gravity). Чтобы значение счетчика не залазило под кнопки и между счетчиками был пустой промежуток, необходимо правильно задать значение отступам и границам счетчика. Чтобы значение счетчика Лайков не залазило под кнопку Лайков задаем отступ слева (paddingLeft), чтобы текст не был вплотную к правой границе задаем отступ справа (paddingRight), а пустого промежутка между счетчиками добиваемся установкой правильной границы справа (layout_marginRight).
  • like_unlike_rating_container – свойства контейнера счетчиков. Чтобы счетчики не прижимались к краям нашего контрола, а располагались по горизонтали между центрами кнопок Лайк и Не лайк, задаем отступы справа и слева для контейнера (paddingLeft, paddingRight). Плюс надо немного поднять счетчики от нижнего края, для этого зададим отступ снизу (paddingBottom)

Кодим

Итак приступаем к самому интересному.  И так у нас есть разметка, есть графические материалы, если стили, осталось написать код, реализующий правильную работу нашего контрола.

Начнем с функции initConrol, которую мы уже обозначили ранее. Задача данной функции настроить котрол. Она должна загрузить содержимое разметки, которую мы создали. Кроме этого, функция находит счетчики и сохраняет на них указатели (для этого в класс были добавлены приватные переменные likeCountView, unlikeCountView), чтобы не тратить время на их поиск (findViewById) каждый раз, когда нам нужно будет модифицировать их значение. Функция настраивает обработчик нажатия на кнопки Лайк и Не лайк. С помощью функции setOnClickListener, мы указали, что наш контрол сам будет обрабатывать нажатия на эти кнопки. Не забываем добавить “implements OnClickListener” к объявлению класса контрола, чтобы он мог быть обработчиком нажатий.

Пора задуматься об интерфейсе связи нашего контрола с внешним миром. Из вне мы должны иметь возможность задать значение счетчикам и иметь возможность реализовать реакцию на нажатия на кнопки Лайк/Не лайк. Сначала реализуем функцию setLikes, с помощью которой можно будет задавать значения счетчикам.

Теперь приступим к передачи во внешний мир информации о событиях. События у нас два: нажатие на кнопку Лайк, нажатие на кнопку Не лайк. Чтобы отличать эти события создадим перечисление ClickEvent. Создадим интерфейс LikeUnLikeOnClickListener, который должен реализовывать обработчик событий. Для установки обработчика событий создадим функцию setOnLikeUnlikeClickListener. Посмотрим, что у нас вышло.

Осталось реализовать внутренний обработчик нажатий. Ранее было упомянуто, что наш контрол должен реализовывать интерфейс onClickListener, приведем реализацию.

Готово. Теперь наш котрол правильно выглядит и у нас есть интерфейс для взаимодействия с ним. Любуемся результатом.

Заключение.

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

На этом я, пожалуй, закончу. Удачных разработок.

П.С. По-хорошему нужно создавать как минимум 3 набора графических материалов и размещать их в папках drawable-hdpi/ldpi/mdpi, тогда ваша графика будет всегда четко выглядеть на экранах с любой плотностью. Но если у вас нет возможности/необходимости делать это, то разместите свои ресурсы в папке drawable-hdpi, Андроид будет сам масштабировать изображения, так чтобы они были одинакового физического размера на экранах с различными значениями плотности.

П.П.С. Было бы так же не лишним добавить возможность задавать значения счетчиков из xml разметки. Но это уже в следующей серии)

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

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