- Полный список
- Level List
- Transition Drawable
- Inset Drawable
- Clip Drawable
- Scale Drawable
- Animation drawable
- Полный список
- Bitmap
- Layer List
- State List
- Clipping in Android. Quickly, qualitatively, cheap.
- Prototypes
- Easy way
- Thorny path
- Criteria for implementing the tool:
- View.clipToOutline
- Canvas.clipPath() method rendering elements in container
- Canvas.drawPath() method rendering elements in container, View.LAYER_TYPE_SOFTWARE
- Canvas.drawPath() method rendering elements in container, View.LAYER_TYPE_HARDWARE
Полный список
Продолжаем разбирать типы Drawable, которые можно описывать с помощью XML-разметки. Проектов в этом уроке создавать не будем. Я просто буду в своем проекте создавать XML-файлы в папке drawable и ставить их фоном для View. А в тексте урока приведу код и скрины. Иногда буду вешать дополнительно серый фон, чтобы был виден реальный размер View.
Чтобы программно добраться до Drawable, который вы для View повесили как фон, надо просто вызвать метод getBackground.
Level List
Тег позволяет отображать Drawable в зависимости от значения level. Рассмотрим пример, где будем отображать три разных Drawable: если level=0, то зеленый прямогуольник; если 1, то желтый; если 2, то красный. Создадим три Drawable
Level List, в котором мы указываем какой Drawable при каком максимальном уровне (maxLevel) отображать.
Менять уровень у Drawable мы можем методом setLevel. Ставим 0 (по умолчанию)
В соответствии с заданным уровнем отображается Drawable.
Кроме атрибута maxLevel можно также использовать атрибут minLevel для указания минимального значения.
Transition Drawable
XML выглядит так:
Также у item есть атрибуты left, top, right, bottom для задания отступа.
В коде, для переключения между Drawable используются методы startTransition и reverseTransition. Оба метода принимают на вход количество мсек, которое будет длиться переход.
Inset Drawable
Насколько я понял – это просто обертка Drawable, которая позволяет указать padding-отступы. Корневой тег – , атрибуты отступа insetLeft, insetTop, insetRight, insetBottom.
Clip Drawable
Тег позволяет обрезать Drawable по горизонтальной и (или) вертикальной оси. Какая часть картинки будет обрезана зависит от значения level. Используемый диапазон тут от 0 до 10000, где 0 — картинка полностью обрезана и не видна, 10000 — картинка видна полностью. Атрибут gravity позволяет указать направление урезания.
Рассмотрим несколько примеров.
Устанавливаем программно (методом setLevel) уровень в 7000. А XML рисуем такой:
Ось — горизонтальная, направление — влево. Т.е. картинка обрезается по горизонтали справа-налево до 70% (7000 от 10000).
Уровень установим в 2000, а xml делаем таким:
Картинка обрезается по вертикали снизу вверх до 20%
Уровень 5000, xml:
Картинка обрезается по обоим осям вправо и вниз до 50%
Уровень 6000, xml:
Картинка обрезается по горизонтали к центру до 60%
Scale Drawable
Тег позволяет сжать картинку по горизонтальной (scaleWidth) и (или) вертикальной (scaleHeight) оси и сместить полученное изображение в указанную часть (scaleGravity) доступного пространства.
У меня этот тип Drawable работал только, если установить ему level > 0.
Сжимаем на 30% по горизонтали и смещаем влево.
Сжимаем на 70% по вертикали и смещаем вниз.
Сжимаем на 50% по обоим сторонам и смещаем вправо-вниз.
Сжимаем на 40% по ширине и 60% по высоте, и смещаем в центр
Animation drawable
Список Drawable с указанием времени отображения для каждого. Атрибут oneShot установленный в true говорит системе о том, что воспроизведение нужно только один раз. Если поставить false, то анимация будет циклично повторяться.
Чтобы запустить процесс анимации, необходимо получить Drawable, привести его к типу AnimationDrawable и вызвать метод start.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Полный список
— изучаем drawable теги: , ,
Продолжаем разбирать типы Drawable, которые можно описывать с помощью XML-разметки. Проектов в этом уроке создавать не будем. Я просто буду в своем проекте создавать XML-файлы в папке drawable и ставить их фоном для View. А в тексте урока приведу код и скрины. Иногда буду вешать дополнительно серый фон, чтобы был виден реальный размер View.
Чтобы программно добраться до Drawable, который вы для View повесили как фон, надо просто вызвать метод getBackground.
Bitmap
Тег позволяет получить Drawable обертку для Bitmap. У тега есть несколько атрибутов.
В атрибуте src указываем нужный нам файл-изображение.
Атрибут gravity указывает, как bitmap будет располагаться внутри Drawable. Можно использовать несколько значений, разделенных знаком | . Значения тут стандартные, и некоторые из них мы часто используем при работе с layout. Рассмотрим пример.
Значение атрибута gravity сдвигает изображение влево-вверх
Далее ставим следующие значение атрибута gravity:
fill_horizontal — растянуть по горизонтали
fill — растянуть (используется по умолчанию)
Насколько я понял, значения clip_vertical и clip_horizontal идентичны значениям fill_vertical и fill_horizontal в случае когда Bitmap по размеру больше, чем предоставляемое ему пространство. Т.е. clip_vertical сожмет его по вертикали, так чтобы он влез. А clip_horizontal — по горизонтали.
Атрибут tileMode — это режим «плитки». Позволяет замостить вашим изображением все доступное пространство. По умолчанию он имеет значение disabled.
Для примера я создам такой bitmap.
Четыре разных цвета, внутренние границы — сплошные, внешние — пунктиром.
Если tileMode = repeat, то Bitmap будет размножен и займет все доступное пространство
Далее меняем значение атрибута tileMode.
mirror – Bitmap также будет размножен, но при этом он будет чередоваться со своим отражением
clamp – растягивает края картинки на все свободное пространство
Прочие атрибуты тега :
antialias – сглаживание линий
dither – преобразование цветов, если текущей палитры недостаточно для отображения
filter – фильтр при сжатии или растягивании (пример результата использования есть в Уроке 158)
mipMap – использование mip-текстурирования. Про него можно почитать в википедии. Используйте этот режим, если планируете в процессе отображения уменьшать bitmap более чем в два раза.
Мы рассмотрели XML-описание, но вы всегда можете создать этот объект и программно. Java-реализация – класс BitmapDrawable.
Layer List
Мы можем описать Drawable, который будет состоять из нескольких Drawable-слоев. Для этого используется тег , а внутри него теги .
У нас 4 слоя. Три bitmap со стандартной иконкой и одна фигура. Атрибуты left, top, right, bottom позволяют указывать отступы. А в атрибуте id можно указать id этого Drawable-слоя.
Обратите внимание, что важен порядок тегов item. Каждый последующий слой рисуется поверх предыдущего. Например, на получившемся изображении видно, что прямоугольник проходит «над» верхней иконкой, но «под» нижней.
Мы можем в коде получать доступ к отдельным Drawable внутри LayerDrawable. Для этого сначала получаем LayerDrawable.
А затем вызываем метод findDrawableByLayerId(int id) и указываем id, который вы указывали в атрибуте id тега item. На выходе получим Drawable.
Также у LayerDrawable есть еще несколько интересных методов
getDrawable(int index) — возвращает Drawable по индексу, а не по id
getId(int index) — возвращает id по индексу
getNumberOfLayers() — возвращает кол-во Drawable-слоев
State List
Тег позволяет отображать Drawable в зависимости от состояния View. Возможные состояние View можно посмотреть в хелпе. Рассмотрим пример с двумя из них: checked и pressed. На экране будет ToogleButton. Эта кнопка переходит в состояние checked и обратно, если на нее нажимать. А во время нажатия, пока палец касается экрана, кнопка находится в состоянии pressed.
State List позволит нам использовать три разных Drawable для отображения кнопки в трех состояниях: обычное, checked, pressed. Для этого создадим три файла в папке drawable.
Прямоугольник темно-серого цвета. Этот Drawable будем отображать в обычном состоянии кнопки.
Прямоугольник темно-синего цвета. Этот Drawable будем отображать в нажатом состоянии кнопки.
Прямоугольник светло-синего цвета. Этот Drawable будем отображать когда кнопка находится в состоянии checked.
И еще один файл, button_selector.xml:
Этот последний Drawable является селектором. В нем мы используем теги item, в которых указываем для какого состояния какой Drawable использовать
В первом item мы указали state_pressed=true, а значит этот item будет выбран системой когда кнопка будет в состоянии pressed. И экране мы увидим Drawable из этого item, т.е. toogle_button_pressed.
В втором item мы указали state_checked=true, а значит этот item будет выбран системой когда кнопка будет в состоянии checked. И экране мы увидим toogle_button_checked.
В третьем item мы не указали никакого состояния, этот item будет выбран при обычном состоянии кнопки. И экране мы увидим toogle_button.
Учтите, что здесь важен порядок расположения item внутри selector. Т.е. система идет по ним по порядку и выбирает первый подходящий. Если вы третий item, который без явного указания состояния, поставите первым, то система всегда будет останавливаться на нем.
Состояния можно комбинировать, т.е. в одном item вы можете указать несколько разных состояний.
Ставим этот Drawable, как фон для ToogleButton:
В результате, сначала видим обычное состояние
Нажимаем и держим, т.е. состояние pressed
Отпускаем – включился checked
Еще раз нажмем-отпустим — выключится checked и будет снова обычное состояние. Для каждого состояния отображается свой Drawable.
У View, кстати, есть методы, которые позволяют программно управлять состоянием. Это, например: setPressed и setSelected.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Clipping in Android. Quickly, qualitatively, cheap.
Hey, I’m Dmitriy Дмитрий Гайдук and I am wokring as an adroid-developer in KODE for more than three years.
Quite often, during mobile app developing process it is required to give the content some form, for example, to make a round image for the avatar. If such a tool like Picasso transformation is enough for ImageView, then it is more difficult for dynamic content. For example, there is a list of elements that can be scrolled and need to be clipped. For this, the CardView widget may well be appropriate.
But what if you need a form other than a rectangle, and at the same time, this form will be dynamically changed? It is the exact case from one of our ongoing projects. I couldn’t find proper UI components and decided to research what Android tools are appropriate for this case.
This is a longread — so the main part as usual in the end of the article.
Prototypes
Our designers are really creative. Sometimes their crazy ideas could puzzle even the most experienced developer. Look at what they prepared this time.
There is a card with some content, which could be scrolled. The card has rounded upper corners and lower corners remain straight. It is possible to pull the card up and when the card fills the entire screen, the rounding is removed. Really easy, at first sight, of course.
Easy way
You can use CardView, but there are some disadvantages which I found during coding:
- you can not set the radius for each corner separately;
- “fair” clipping works only on versions starting from Android Lollipop. Before this version, the content is indented by the size of the corner radius. This is noticeable when scrolling, spaces appear between the edges of the card;
- setting zero radius, an application running on a device with a version below Andrid Lollipop crashes. In the documentation, such cases are simply not described.
Thorny path
So, standart widget is useless. It’s time to look for the propriate way out and develop the tool by yourself. But first of all, we need to sort out woth all possibilities for this case in other Android tools.
Criteria for implementing the tool:
- ability to use at Android versions before and after Lollipop. In extreme cases, to combine approaches;
- ability to set any form. For example, using the Path class;
- presence of smooth lines and the possibility of antialiasing;
- performance. The shaping of the content should occur with minimal impact on the performance of the device, especially for dynamically changing content and form.
Of course, it is an ideal version of the tool. I researched 4 differents variants and all of them has pros and cons, but they are suitable for part of cases:
- View.clipToOutline
- Canvas.clipPath() method when rendering elements in container.
- Canvas.drawPath() method when rendering elements in container. Settings: PorterDuff.Mode + View.LAYER_TYPE_SOFTWARE
- Canvas.drawPath()method when rendering elements in container. Settings: PorterDuff.Mode + View.LAYER_TYPE_HARDWARE
View.clipToOutline
This is how CardView works.
How it works:
Set the background image of the desired form and set it in View.clipToOutline value = “true”.
We know that we work with API Android and the easiest way couldn’t be right. Look at documentation 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()
It is enough for Outline.canClip() to return true . Look documentation for this method too:
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.
So there are quite limited set of forms and it couldn’t realize all requirements of design.
To dynamically change the shape of the background image, you can create a class derived from Drawable. In getOutline() method set the desired outline.
- Good performance
- Line smoothing.
- Works only on Lollipop version and higher;
- Only a rectangle, an oval, and a rectangle with rounded corners
Suitable for cases whan the app is developed for Android Lollipop or higher version and you you need to set the shape of a rectangle, an oval and a rectangle with rounded corners. Perhaps in the future there will be support for any form of contour.
Canvas.clipPath() method rendering elements in container
This variant requires inheritance from container class. In dispatchDraw() method do the trimming in the desired shape using Canvas.clipPath() method.
Set the required contour using the object of Path class. The flaws are visible after the launch of the application — there is no antialiasing, and it is unclear how to add it.
- Works on versions before\after Android LOllipop;
- Good perfomance.
The variant is simple to implement and is suitable for cases when you need to add the functionality to the project quickly, and antialiasing is unprincipled.
Canvas.drawPath() method rendering elements in container, View.LAYER_TYPE_SOFTWARE
This variant also requires inheritance from container class. We do trimming to the right shape in dispatchDraw() method using Canvas.drawPath() method with outline and the Paint class object in parameters. Paint object initializes with xfermode property.
PorterDuffXfermode(PorterDuff.Mode.CLEAR) objest allows to cut the desired area on the screen using the blend mode of one image to another. What for then setLayerType(View.LAYER_TYPE_SOFTWARE, null) ? The fact is that this mode does not work correctly in other types of Layer. Otherwise, behind the cropped figure there will be a white background, and we need a transparent one.
Detailed documentation of PorterDuff.
Accordingly, this setup leads to a performance drop. But how critical it is, can be seen on the performance graph.
During testing, it became clear that no longer work things like a shadow at the button. It seems that the problem is in View.LAYER_TYPE_SOFTWARE.
- Works on versions before\after Android Lollipop;
- Smooth lines.
- Bad performance
- The shadow is not displayed on the buttons. It looks like the problem is in View.LAYER_TYPE_SOFTWARE;
- Not all types of PorterDuff.Mode work
Canvas.drawPath() method rendering elements in container, View.LAYER_TYPE_HARDWARE
The key point, as in the previous approach, is overriding the dispatchDraw () method, in which we use the canvas.drawPath () method for trimming. We set up the View.LAYER_TYPE_HARDWARE for PorterDuffXfermode correct work, you need to add additional logic with the Canvas object in the dispatchDraw () method. Thanks for the hint Ilya Nekrasov
- Works on versions before\after Android Lollipop;
- Good performance;
- Smooth lines.
- If there are two View in container and Alpha changes in each of them, then flicker sometimes occurs. There is no flicker in any version of Android before Lollipop.
To solve the problem with clipping in our project, we used this method. We got rid of flickering when switching between screens, by creation a container for each screen. This affected the performance when changing screens, but not critical and almost imperceptible, since the transition takes less than a second.
Источник