Закруглить только угол android

Скругление углов в Android. Быстро, качественно, недорого.

Привет, меня зовут Дмитрий @dmitriy1984g , я больше трех лет разрабатываю android-приложения в калининградской компании KODE.

Довольно часто при разработке мобильных приложений требуется придавать содержимому некую форму, например, сделать круглое изображение для аватарки. Если для статичных элементов (ImageView) можно обойтись таким инструментом как Picasso transformation, то для динамического содержимого дела обстоят сложнее. Например, есть список элементов, который можно прокручивать и нужно закруглить края. Для этого вполне может подойти виджет CardView.

Но что если нужно задать форму отличную от прямоугольника, и при этом эта форма будет динамически меняться? Это и потребовалось сделать в одном из наших проектов. Подходящих стандартных компонентов UI для данных целей не нашлось, что сподвигло меня на исследование, какие вообще есть инструменты Android для придания формы контенту.

Статья длинная — для тех, кому лень читать всё, самое важное и исходный код в конце статьи.

Дизайнерская верстка

В нашей компании работают довольно креативные дизайнеры. Их безумные идеи порой могут озадачить даже самого опытного разработчика. Посмотрим, что на этот раз нам приготовили.

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

Протоптанная дорожка

На первый взгляд, можно использовать виджет CardView, но в процессе разработки был выявлен ряд недостатков:

  • нельзя задать радиус для каждого угла отдельно;
  • “честное” закругление работает только на версиях, начиная с Android Lollipop. До этой версии добавляется отступ содержимого на размер радиуса углов. Это заметно при прокрутке, появляются пробелы между краями карточки;
  • при установке нулевого радиуса приложение, запущенное на устройстве с версией ниже Andrid Lollipop, падает. В документации такие случаи просто не описаны.

Тернистый путь

Итак, стандартный виджет нам не подходит. Придется искать подходящее решение и писать инструмент самостоятельно. А для начала разобраться, какие есть возможности в этом направлении у других Android инструментов.

Критерии для реализации инструмента:

  • возможность использовать в версиях Android до Lollipop и после. В крайнем случае комбинировать подходы;
  • возможность задавать любую форму. Например, с помощью класса Path;
  • наличие плавных линий и возможности сглаживания (antialiasing);
  • и конечно же, производительность. Придание формы содержимому должно происходить с минимальным воздействием на производительность устройства, особенно для динамически меняющегося содержимого и формы.

Это, конечно, был бы универсальный инструмент. В вариантах, которые я исследовал, не все критерии соблюдаются, но для реализации части случаев вполне подходят.

Я попробовал 4 различных варианта:

  • Свойство View.clipToOutline
  • Метод Canvas.clipPath() при отрисовке дочерних элементов в контейнере
  • Метод Canvas.drawPath() при отрисовке дочерних элементов в контейнере. Настройки PorterDuff.Mode + View.LAYER_TYPE_SOFTWARE
  • Метод Canvas.drawPath() при отрисовке дочерних элементов в контейнере. Настройки PorterDuff.Mode + View.LAYER_TYPE_HARDWARE

Свойство View.clipToOutline

Этот подход используется в виджете CardView. Глянем, что он умеет.

Как это работает:
Задаем фоновое изображение нужной формы и выставляем в поле View.clipToOutline значение “true”.
Но мы знаем, что работаем с API Android, и слишком легко, чтобы быть правдой. Смотрим документацию метода View.setClipToOutline()

Sets whether the View’s Outline should be used to clip the contents of the View…Note that this flag will only be respected if the View’s Outline returns true from Outline.canClip()

Всего-то достаточно, чтобы Outline.canClip() возвращал true . Смотрим документацию и для этого метода:

Returns whether the outline can be used to clip a View. Currently, only Outlines that can be represented as a rectangle, circle, or round rect support clipping.

Получается, что пока доступен ограниченный набор форм. Вот так, задумка хорошая, но возможности не позволяют реализовать весь полет фантазий.
Для того, чтобы динамически менять форму фонового изображения, можно создать класс, наследованный от Drawable. В методе getOutline() задаем нужный контур.

Читайте также:  Taken 3 для android

  • Хорошая производительность;
  • Сглаживание линий.
  • Работает только на Lollipop и выше;
  • Ограничение форм: прямоугольник, овал и прямоугольник со скругленными краями.

Хорошо подходит для ситуации, когда нужно задать форму прямоугольника, овала и прямоугольника со скругленными краями, при условии, что приложение разрабатывается для версий Android Lollipop и выше. Возможно, в будущем появится поддержка любой формы контура, но это в будущем.

Метод Canvas.clipPath() при отрисовке дочерних элементов в контейнере

Данный подход состоит в наследовании от класса контейнера. В методе dispatchDraw() делаем обрезку по нужной форме с помощью метода Canvas.clipPath()

Все что нужно — это задать необходимый контур с помощью объекта класса Path . Недочеты видны после запуска приложения — отсутствует сглаживание линий (antialiasing), а как его добавить — непонятно.

  • Работает на версиях до/после Lollipop;
  • Хорошая производительность.

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

Метод Canvas.drawPath() при отрисовке дочерних элементов в контейнере, View.LAYER_TYPE_SOFTWARE

Этот подход также требует наследования от контейнера. В методе dispatchDraw() делаем обрезку по нужной форме с помощью метода Canvas.drawPath() , в который передаем контур и объект класса Paint с инициализацией свойства xfermode .

Объект PorterDuffXfermode(PorterDuff.Mode.CLEAR) позволяет вырезать нужную область на экране, используя режим наложения одного изображения на другое. Зачем тогда вызов setLayerType(View.LAYER_TYPE_SOFTWARE, null) ? Дело в том, что этот режим некорректно работает в других типах Layer. В ином случае за обрезанной фигурой будет белый фон, а нам нужен прозрачный.
Подробнее про PorterDuff в документации .

Соответственно, данная настройка ведет к падению производительности. Но насколько это критично, видно на графике производительности.

В процессе тестирования выяснилось, что перестают работать такие вещи как тень у кнопки. Похоже, проблема именно в View.LAYER_TYPE_SOFTWARE.

  • Работает на версиях до/после Lollipop;
  • Гладкие, плавные линии.
  • Плохая производительность;
  • Не отображается тень на кнопках. Похоже, проблема именно в View.LAYER_TYPE_SOFTWARE;
  • Работают не все виды PorterDuff.Mode.

Метод Canvas.drawPath() при отрисовке дочерних элементов в контейнере, View.LAYER_TYPE_HARDWARE

Ключевым моментом, как и в предыдущем подходе, является переопределение метода dispatchDraw() , в котором используем метод canvas.drawPath() для обрезки. Для корректной работы PorterDuffXfermode при настройке View.LAYER_TYPE_HARDWARE нужно дописать дополнительную логику с объектом Canvas в методе dispatchDraw() .За подсказку спасибо Ilya Nekrasov

Как это выглядит в реализации:

  • Работает на версиях до/после Lollipop;
  • Хорошая производительность;
  • Гладкие, плавные линии.
  • Иногда происходит мерцание, если в контейнере два View и у каждого меняется Alpha, т.е. один объект появляется, другой исчезает. При этом объект, который исчезает, может на доли секунды появляться при 100% Alpha. Мерцание не замечено в версии Android до Lollipop.

Для решения проблемы с закруглением углов в нашем проекте мы использовали данный метод. Избавились от мерцания при переходе между экранами, создав для каждого экрана свой контейнер. Это сказалось на производительности при смене экранов, но не критично и практически незаметно, так как переход занимает менее секунды.

Читайте также:  Keyboard pro для android

Схематично было так:

Данный подход может быть применен для большинства задач. Учитывая, что в объекте Path можно задать любую форму, это дает практически безграничные возможности. Радует и относительно хорошая производительность. Но из-за дополнительных манипуляций с объектом Canvas , могут возникать побочные эффекты, как это произошло у нас.

Extra

Получив достаточно информации по методам отсечения в Android, хочется проверить, как это работает с изменяемой формой в виде анимации. За основу анимации взял следующее: трансформация звезды в многоугольник и обратно в звезду. Все это вращается вокруг своего центра. Посмотрим, что получилось и как это влияет на производительность.

Метод PorterDuff.Mode + View.LAYER_TYPE_HARDWARE

Метод Canvas.clipPath()

Мнение автора

Каждый из подходов хорошо применим в определенной ситуации.

Больше всего мне понравился подход со свойством View.clipToOutline , так как не нужно делать наследование от классов и он прост в реализации, а возможность задавать форму с помощью объекта Drawable упрощает задание контура для обрезки. Хотелось бы надеяться, что в будущем расширятся возможности для задания формы с помощью View.clipToOutline .

Интересный подход с методом Canvas.clipPath() . Минимум кода для реализации, менее “прихотлив” к версии Android, можно задавать любую форму. Если бы можно было добавить Antialiasing, то это было бы практически идеальным решением.

Что не советовал бы использовать, так это метод с View.LAYER_TYPE_SOFTWARE PorterDuffXfermode — сплошные минусы.

И как более универсальное средство — это реализация с помощью View.LAYER_TYPE_HARDWARE и PorterDuffXfermode . Данное решение работает на всех версиях Android, довольно хорошая производительность, можно задать любую форму контура, но нужно помнить об предостережениях, описанных выше.

В целом, результат исследования меня не особо обрадовал. Может быть я слишком требователен. 😀 Да, мы нашли способ сделать то, что было задумано, но хотелось бы более простого решения, так как наследование не самый лучший вариант. И к тому же нужен более удобный в использовании метод, как в случае с View.clipToOutline . Возможно, в будущем Android API этим нас обрадует. Ну или, может, это проще сделать в Flutter и пора переходить на него?🙂

Ух, это была довольно большая статья. Кто дочитал, тому виртуальный приз 🍬

Ссылка на исходный код . Тестовое устройство: Xiaomi Redmi 4x

Удачного кода, меньше багов, больше счастливых пользователей!

Источник

Закругляем экраны на android-устройствах: Cornerfly, Roundr и Rounded Corner

Оглавление

Вступление

Инженеры и маркетологи пытаются всевозможными способами выделить устройства своей компании среди конкурентов, пуская в ход дизайн, начинку и софт. Но получается это далеко не у всех. Впрочем, новинки LG и Samsung, а точнее G6, S8 и другие модели Galaxy все-таки сумели превзойти оппонентов за счет дисплеев с закругленными краями. Это сразу бросается в глаза, как только вы берете смартфон в руки и включаете его.

реклама

У моделей G6 и S8 экраны по-настоящему закруглены по краям, но еще в далеких 2011-2012 годах в мобильных устройствах Xiaomi на базе OC Miui была функция «Закругление краев экрана», реализованная программным образом. И если сейчас это стало трендом, так почему бы и нам не «обновить» свое устройство? Сделать это можно с помощью специальных приложений, коих недавно появилось достаточно.

Для «скругления» краев были отобраны три добротных утилиты, каждая из которых выполняет поставленные задачи. Но какая программа справится со своей задачей лучше? Мы протестируем Cornerfly, Roundr и Rounded Corner, а затем по итогам обзора оформим выводы.

Читайте также:  Dimen in android studio

В качестве тестового оборудования использовались следующие устройства:

  • Смартфон Xiaomi Redmi Note 3 Pro (OC Android 7.1, Resurrection Remix 5.8.х, процессор Snapdragon 650 64 бит, 6 х 1800 МГц, видеосопроцессор Adreno 510, 2 Гбайт ОЗУ);
  • Смартфон Jinga Basco M500 3G (OC Android 5.1, процессор MediaTek MT6580, 4 х 1300 МГц, видеосопроцессор Mali-400 MP2, 1 Гбайт ОЗУ).

Cornerfly

Знакомство

Хотите сделать внешний вид своего устройства более интересным, необычным и утонченным? Пожалуйста, к вашим услугам утилита для скругления краев экрана, известная под именем Cornerfly.

реклама

Работа приложения

При первом запуске любой утилиты, так или иначе затрагивающей работу устройства, потребуется дать ей необходимые права. В случае с Cornerfly это разрешение наложения элементов и специальных возможностей для доступа к системным ресурсам. В принципе, ничего необычного.

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

Поскольку это не какое-то офисное приложение и не файловый менеджер, то и рассматривать тут особо нечего, поэтому мы плавно перейдем к всевозможным настройкам и параметрам утилиты.

Для начала, с учетом разных вкусовых предпочтений пользователей, разработчики предлагают выбрать размер уголков. Для этого предусмотрено сто градаций, так сказать, широчайший спектр настроек. Правда, по умолчанию стоит размер 12 единиц, а изменить его можно только в расширенной версии данной программы. Естественно, она не бесплатная.

Следующий параметр – это цвет уголков. На аппарате с черной передней панелью эта настройка практически не нужна, но если панель у вас белого, красного, серого или какого-то другого цвета, то подстроив показатели RGB, вы сможете добиться слияния уголков экрана с передней панелью. Это позволит добиться того самого эффекта, когда кажется, что экран с закругленными краями.

Предусмотрены настройки отображения уведомлений и работы приложения в различных режимах, например, при открытии программы или игры в полноэкранном режиме. Рекомендуется включить эти параметры для корректной работы утилиты, если же возникнут проблемы – отключить.

Но, вероятно, в играх и каких-то специализированных программах вам может потребоваться вся площадь экрана и даже края. Поэтому заранее можно настроить автоотключение скруглений в том или ином приложении.

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

Тестирование

реклама

Работа Cornerfly была протестирована в разных режимах и с различными приложениями, и программа меня не подвела. Что в альбомной ориентации, что в портретной, что при запуске полноэкранной игры она без каких-либо проблем справлялась с поставленной задачей.

Версия приложения 1.13.RC1
Размер дистрибутива 2.75 Мбайт
Размер приложения в установленном виде 8.16 Мбайт
Потребление ОЗУ 0-50 Мбайт

реклама

Вы бы очень удивились, если бы я назвал данную программу требовательной или ресурсоемкой. И, в принципе, такой ее назвать сложно. В установленном виде она занимает около 8 Мбайт, в режиме работы – около 50 Мбайт ОЗУ, а на работу процессора и расход аккумуляторной батареи практически не влияет.

Выводы

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

Источник

Оцените статью