- Manipulating images and Drawables with Android’s ColorFilter
- Tinting, custom effects and reusability for visual Android app resources
- Where can this be used? 🤔
- Canvas
- Drawable
- ImageView
- Introducing our sample image 🖼️
- PorterDuffColorFilter 1️⃣
- LightingColorFilter 2️⃣
- ColorMatrixColorFilter 3️⃣
- Limitations and other options 🛠️
- Android Compose Image Tutorial
- Android Jetpack Compose – Image
- Example
- Image Tutorials
- Conclusion
- Первое впечатление от Android Jetpack Compose
- Что такое Android Jetpack Compose?
- 1. Независимость от релизов Android
- 2. Весь API на Kotlin
- 3. Composable = Композитный: использование композиции вместо наследования
- 4. Однонаправленный поток данных
- 5. Улучшение отладки
- Хватит слов, покажите код
- 1. FrameLayout vs Wrap + Padding + Background
- 2. Вертикальный LinearLayout vs Column
- 2a. Отступы
- 2b. Интервалы
- 2c. Горизонтальный LinearLayout vs Row
- 2d. Gravity vs Alignment
- 2d. Замечание
- 3.Стили и темы
- Что посмотреть/почитать
- Выводы
Manipulating images and Drawables with Android’s ColorFilter
Tinting, custom effects and reusability for visual Android app resources
Image and Drawable resources are an integral part of any Android app. Since day (i.e. API level) 1, the Android framework has provided a means of manipulating the colors of these assets on a per-pixel level, as they are drawn to screen, with the ColorFilter class.
The documentation for the base abstract class stipulates that “a color filter can be used with a Paint to modify the color of each pixel drawn with that paint”. This is a powerful tool that can be used for:
- Applying effects and adjustments to images
- Tinting of icons and logos
Ultimately this encourages the good practice of reusing resources for scenarios that require different color variations, resulting in reduced app size.
Note: ColorFilter is an abstract class that should never be used directly, and the constructor was deprecated in API level 26. Only the subclasses should be used, which we will discuss further down.
Where can this be used? 🤔
Before we explore the existing subclasses and their capabilities, we need to discuss where we can actually use ColorFilter .
Canvas
As hinted at in the documentation description, we can use ColorFilter when drawing to Canvas in a custom View . Specifically, a ColorFilter is set on a Paint which then affects everything that is drawn with it:
Drawable
A ColorFilter can be applied to a Drawable , which will affect how it is rendered in View s and layouts:
In addition to this, there exists a Drawable convenience function to apply a PorterDuffColorFilter (more on that later):
Lastly, it is important to note that a tint can be applied to a Drawable . This is separate and will be overridden if a ColorFilter is set via either one of the above functions.
ImageView
A ColorFilter can be applied to an ImageView , which will affect how its current image will be rendered:
As with Drawable , convenience functions exist for applying a PorterDuffColorFilter :
Introducing our sample image 🖼️
We are now going to dive into the three subclasses of ColorFilter ( PorterDuffColorFilter , LightingColorFilter and ColorMatrixColorFilter ). In order to demonstrate this, we will make use of a visual aid in the form of a sample image:
It has photographic detail while also having “shape” as a result of the transparent areas. This will allow all of the subclasses to be demonstrated.
PorterDuffColorFilter 1️⃣
We have already touched on this briefly. This is a color filter that accepts a single color that is applied to all source pixels along with a Porter-Duff composite mode. There are many modes that are suitable to different scenarios. Typically, this is used to apply a “blanket” color (eg. To tint an icon).
LightingColorFilter 2️⃣
This color filter can be used to simulate lighting effects on an image. The constructor accepts two parameters, the first to multiply the source color ( colorMultiply ) and the second to add to the source color ( colorAdd ).
While the source color alpha channel is not affected, the R, G and B channels are computed like so:
Note: The above is using Android KTX Color extension functions for accessing the red, blue and green channels (alpha is also available).
ColorMatrixColorFilter 3️⃣
This is arguably the most flexible (but also the most complex) color filter.
It is quite similar to LightingColorFilter , in that we can tweak each pixel’s color channels using values that are multiplicative and additive. However, a ColorMatrixColorFilter is constructed with a ColorMatrix , which is essentially a wrapper class for a 4×5 matrix. This gives us 20 values, used in a certain way, that allow us to transform colors using all of the existing channels, including alpha.
Before we dive into using matrices, let’s first take a look at some of the convenience functions offered by ColorMatrix :
Fear not if some of these color concepts do not make sense! The point here is that ColorMatrix offers a lot of flexibility, and these convenience functions are simply matrix operations under the hood.
We know that ColorMatrix wraps a 4×5 matrix. Given this matrix, how do we arrive at our resultant color channels? They are computed like so:
The 4 rows in fact represent the resultant R, G, B and A channels. For each channel, the 5 columns allow you to combine the existing R, G, B, and A channels and a wildcard value in a plethora of ways.
This provides us with a lot of possibilities. We can adjust the brightness/contrast of images, ignore some channels, invert an image or even create basic filters and effects.
Limitations and other options 🛠️
While ColorFilter is powerful and flexible, image manipulation is a vast field and it simply cannot cover everything.
For example, the current pixel value that is being passed through the filter would be handy to know, but is not available. Additionally, you are limited to the three subclasses that ColorFilter currently provides. It appears as if you cannot create a custom subclass, due to native code restrictions.
In instances like this, what other options do we have?
The graphics framework has other useful classes such as Shader and MaskFilter . We could turn to RenderScript , which offers Bitmap utilities that still keep us mostly within traditional Android graphics territory. There is also OpenGL, which is perhaps the most extreme power vs. complexity tradeoff, but opens up all of the possibilities of custom GLSL shaders.
Overall, ColorFilter is still a fantastic tool for working with app resources.
I hope this post has provided some insight into ColorFilter and the flexibility it provides when working with images and Drawable s. If you have any questions, thoughts or suggestions then I’d love to hear from you!
Источник
Android Compose Image Tutorial
Android Jetpack Compose – Image
Image composable function is used to display image in Android application.
The most basic code of an Image composable to display an image from drawable resources is as shown in the following.
We can also modify the shape or its look using optional parameters.
Example
Add an image to the Resources using Resource Manager. You may drag and drop the image in Drawable section in Resource Manager as shown in the following picture.
Go through the steps, and the image would be added to the resources.
We shall display this image in our Application using Image composable.
MainActivity.kt
Screenshot
Image Tutorials
The following is a list of tutorials, that cover styling and how-to concepts with Text composable.
Conclusion
In this Android Jetpack Compose Tutorial, we learned what Image composable is, and how to style the Image composable, with the help of individual tutorials for each concept.
Источник
Первое впечатление от Android Jetpack Compose
После того, как на Google IO 2019 я увидел доклад про Android Jetpack Compose, захотелось сразу же его попробовать. Тем более, что подход, реализованный в нём, очень напомнил Flutter, которым я интересовался ранее.
Сама библиотека Compose находится в пре-альфа стадии, поэтому доступно не так много документации и статей про нее. Далее я буду полагаться на несколько ресурсов, которые мне удалось найти, плюс открытые исходники библиотеки.
Вот эти ресурсы:
Что такое Android Jetpack Compose?
Раньше весь UI в Android был основан на классе View. Так повелось с первых дней Android. И в связи с этим накопилось множество легаси и архитектурных недостатков, которые могли бы быть улучшены. Но сделать это достаточно сложно, не сломав весь код, написанный на их основе.
За последние годы появилось множество новых концептов в мире клиентских приложений (включая веяния Frontend-а), поэтому команда Google пошла радикальным путём и переписала весь UI-уровень в Android с нуля. Так и появилась библиотека Android Jetpack Compose, включающая в себя концептуальные приёмы из React, Litho, Vue, Flutter и многих других.
Давайте пройдемся по некоторым особенностям существующего UI и сравним его с Compose.
1. Независимость от релизов Android
Существующий UI тесно связан с платформой. Когда появились первые компоненты Material Design, они работали только с Android 5 (API21) и выше. Для работы на старых версиях системы необходимо использовать Support Library.
Compose же входит в состав Jetpack, что делает его независимым от версий системы и возможным для использования даже в старых версиях Android (как минимум с API21).
2. Весь API на Kotlin
Раньше приходилось иметь дело с разными файлами, чтобы сделать UI. Мы описывали разметку в xml, а затем использовали Java/Kotlin код, чтобы заставить ее работать. Затем мы снова возвращались в другие xml-файлы для того чтобы задать темы, анимацию, навигацию,… И даже пытались писать код в xml (Data Binding).
Использование Kotlin позволяет писать UI в декларативном стиле прямо в коде вместо xml.
3. Composable = Композитный: использование композиции вместо наследования
Создание кастомных элементов UI может быть довольно громоздким. Нам необходимо унаследоваться от View или его потомка и позаботиться о многих важных свойствах перед тем, как он правильно заведется. Например, класс TextView содержит около 30 тысяч строк Java-кода. Это связано с тем, что он содержит множество лишней логики внутри себя, которую наследуют элементы-потомки.
Compose подошел с другой стороны, заменяя наследование композицией.
Padding как нельзя лучше подойдет для иллюстрации того, о чем речь:
В существующем UI для того, чтобы отрисовать TextView c отступами в 30dp :
нам нужно написать следующий код:
Это означает, что где-то внутри TextView.java или его суперклассов содержится логика, которая знает, как посчитать и отрисовать отступы.
Давайте посмотрим, как можно сделать то же самое в Compose:
Изменения
TextView стал просто Text() . Свойство android:padding превратилось в Padding , который оборачивает Text .
Преимущества
Таким образом, Text отвечает только за отрисовку непосредственно текста. Он не знает про то, как считать отступы. С другой стороны, Padding отвечает только за отступы и ничего больше. Он может быть использован вокруг любого другого элемента.
4. Однонаправленный поток данных
Однонаправленный поток данных является важным концептом, если мы говорим, например, про управление состоянием CheckBox в существующей системе UI. Когда пользователь тапает на CheckBox , его состояние становится checked = true : класс обновляет состояние View и вызывает callback из кода, который следит за изменением состояния.
Затем в самом коде, например, во ViewModel , вам нужно обновить соответствующую переменную state . Теперь у вас есть две копии нажатого состояния, которые могут создать проблемы. Например, изменение значения переменной state внутри ViewModel вызовет обновление CheckBox , что может закончиться бесконечным циклом. Чтобы избежать этого, нам придется придумывать какой-то костыль.
Использование Compose поможет решить эти проблемы, так как в его основе заложен принцип однонаправленности. Изменение состояния будет обрабатываться внутри фреймворка: мы просто отдаем модель данных внутрь. Кроме того, компонент в Compose теперь не меняет свое состояние самостоятельно. Вместо этого он только вызывает callback, и теперь это задача приложения изменить UI.
5. Улучшение отладки
Так как теперь весь UI написан на Kotlin, теперь можно дебажить UI. Я не попробовал это сам, но в подкасте говорили, что в Compose работают дебаггер и точки остановки.
Хватит слов, покажите код
Я знаю, хочется поскорее увидеть, как выглядит UI в коде (спойлер: очень похоже на Flutter, если вы пробовали писать на нем).
Мы начнем с создания нескольких простых View , затем сравним как они выглядят в существующем UI и в Compose.
1. FrameLayout vs Wrap + Padding + Background
Переиспользуем наш пример выше и попробуем сделать этот TextView с отступами в 30dp и бирюзовым фоном:
Теперь посмотрим на код, который делает то же самое в Compose:
Здесь появляется несколько новых вещей. Так как Text знает только про рендеринг текста, он не заботится об отступах и фоне. Поэтому, чтобы добавить их, нам нужно использовать три отдельные функции:
- DrawRectangle отрисовывает фон
- Padding отрисовывает отступы
- Wrap — функция, которая накладывает параметры друг на друга, как FrameLayout .
Легко. Но немного отличается от существующей UI-системы, к который мы все привыкли.
2. Вертикальный LinearLayout vs Column
Теперь попробуем сделать что-то эквивалентное нашему старому доброму LinearLayout .
Чтобы поместить два элемента один под другим, как на картинке ниже, мы можем использовать Column :
Код будет выглядеть так:
Вложенные в Column элемент будут расположены вертикально друг под другом.
2a. Отступы
Вероятно, вы заметили, что текст и кнопка расположены слишком близко к краю. Поэтому добавим Padding .
2b. Интервалы
Мы можем также добавить немного отступов между Text и Button :
Как выглядит наш экран теперь:
2c. Горизонтальный LinearLayout vs Row
Поместим вторую кнопку рядом с первой:
Внутри Row две кнопки будут расположены горизонтально. WidthSpacer добавляет расстояние между ними.
2d. Gravity vs Alignment
Выровняем наши элементы по центру, как это делает gravity в текущем UI. Чтобы показать diff, я закомментирую старые строки и заменю их новыми:
У нас получится:
С crossAxisAlignment = CrossAxisAlignment.Center вложенные элементы будут выравнены по горизонтали по центру. Мы должны также выставить Row параметр mainAxisSize = FlexSize.Min , похожий по поведению на layout_width = wrap_content , чтобы он не растягивался по всему экрану из-за дефолтного mainAxisSize = FlexSize.Max , который ведет себя как layout_width = match_parent .
2d. Замечание
Из того, что мы видели в примерах выше, можно заметить, что все элементы строятся композитно из отдельных функций: padding — отдельная функция, spacer — отдельная функция, вместо того, чтобы быть свойствами внутри Text , Button или Column .
Более сложные элементы, такие как RecyclerView или ConstraintLayout находятся в разработке: поэтому я не смог найти пример с ними в демонстрационных исходниках.
3.Стили и темы
Вы, вероятно, заметили, что кнопки выше по умолчанию фиолетовые. Это происходит потому, что они используют стили по умолчанию. Рассмотрим, как работают стили в Compose.
В примерах выше FormDemo помечена аннотацией @Composable . Теперь я покажу, как этот элемент используется в Activity :
Вместо функции setContentView() мы используем setContent() — функция-расширение из библиотеки Compose.kt .
CraneWrapper содержит дерево Compose и предоставляет доступ к Context , Density , FocusManager и TextInputService .
MaterialTheme позволяет кастомизировать тему для элементов.
Например, я могу изменить основной цвет темы (primary color) на каштановый следующим образом:
Теперь наш экран будет выглядеть так:
Другие цвета и шрифты, которы можно поменять: MaterialTheme.kt#57
Rally Activity содержит хороший пример, как можно кастомизировать тему: source code to RallyTheme.kt
Что посмотреть/почитать
Если вы хотите большего, вы можете собрать проект-образец по инструкции тут.
Как пишут пользователи Windows, сейчас не существует официального способа запустить Compose, но есть неофициальный гайд из kotlinlang Slack.
Вопросы про Compose можно задать разработчикам в канале #compose kotlinlang Slack.
Оставляйте другие ссылки в комментариях — самые полезные добавлю сюда.
Выводы
Разработка этой библиотеки идет полным ходом, поэтому любые интерфейсы, показанные здесь могут быть изменены. Остается еще множество вещей, про которые можно узнать в исходном коде, как например @Model и Unidirectional data flow (однонаправленный поток данных). Возможно, это тема для будущих статей.
Источник