- From Kotlin synthetics to Android ViewBinding: the definitive guide
- Binding your views like a pro in 2 minutes.
- Setup
- Enable view binding
- Remove android extensions plugin
- Parcelize annotations
- Activities
- Fragments
- ViewHolder
- Includes
- With a view group
- With a
- Conclusion
- Kotlin Android Extensions: полное руководство
- Что такое Kotlin Android Extensions?
- Как этим пользоваться
- Включаем Kotlin Android Extensions в наш код
- Восстанавливаем view из XML
- Магия Kotlin Android Extensions
- Kotlin Android Extensions ― работа с фрагментами
- Kotlin Android extensions и Custom View
- Восстанавливаем views из другого view
- Kotlin Android Extensions версии 1.1.4
- Применяем на ViewHolder (или любом пользовательском классе)
- Kotlin Android Extension. Реализация Parcelable
- Настройка кэша
- Заключение
From Kotlin synthetics to Android ViewBinding: the definitive guide
Binding your views like a pro in 2 minutes.
Since Kotlin 1.4.20, JetBrains deprecated the kotlin-android-extensions plugin in favor of Android View Binding solution.
If you have been using this plugin, it’s time to move on before this feature is completely removed (or switch to Compose?).
For having completely refactored our Android application to use View Binding, we know that it can be somehow painful and sometimes it looks less simple with this new solution. For example, the binding of your view will have to be done differently if you’re working with an Activity or a Fragment. If your layout uses include there are some tricky ways to handle them too.
That’s why we’ve been implementing some convenient methods to make it simpler.
Setup
If you’re refactoring your whole application code, we advise you to do it module per module to avoid committing too many updates.
Enable view binding
To enable the ViewBinding feature, you just need to enable this build feature on your module build.gradle files:
Remove android extensions plugin
If you’ve been using kotlin-android-extensions before, remove the plugin from the build.gradle file too:
Don’t forget to also remove the Android Extensions experimental flag:
Parcelize annotations
The Parcelize annotations have been moved to another package & the previous one has been deprecated.
So, if you used the Kotlin Parcelize annotation to automatize the implementation of your Parcelable objects, you’ll need to use the new plugin :
Then, replace imports in your code from kotlinx.android.parcel to kotlinx.parcelize.
Activities
To set up an Activity to use your binding class you basically have to:
1- Declare a late init variable for your binding class.
2- In the onCreate method, inflate your layout using the static inflate method from the generated Binding class.
3- Call the Activity setContentView with the root view from your binding class.
We can definitely make it way shorter using some extensions:
Our viewBinding method is a simple delegate that inflates lazily the view of the activity by using the inflater method given as a parameter.
In the Activity we just need to declare the binding as a simple val property and set the content view of the activity.
Fragments
Using View Binding in Fragments is quite less easy as you have to do many things to make it works:
- 1- Declare a var property for your binding class instance.
- In the onCreateView callback, inflate the layout for your binding class & return the root view of it.
- To avoid potential leaks, reset your binding class to null in the onDestroyView callback.
As per the official documentation, it should look like that:
That’s a lot of code to add & you should also pay attention to potential leaks on the binding class. This can also be disturbing as the method is quite different from using it in an Activity. Note that you also need to use the !! operator to hide the nullability of the original field.
There’s a lot of room for improvement here and I found a way better solution to deal with it in 1 line:
Yep, really simple!🚀
Here are some explanations:
- We pass to the Fragment constructor the id of our fragment layout, which will be inflated for us.
- We declare the binding property that will handle the fragment lifecycle work for us.
Have a look at this Gist:
Our viewBindingWithBinder simply instantiate a delegate object that will bind the view of our fragment lazily. As the Fragment already inflate the view from the layout id that we pass to it in its constructor, we just need to bind our binding class from it.
The FragmentAutoClearedValueBinding class is the one that will be responsible to clear the reference to the binding class when the fragment gets destroyed by adding an observer to its lifecycle. In the getValue method, we bind the view to our binding class the first time we access our binding property from our fragment.
ViewHolder
If you’ve been using Kotlin synthetics, you probably have something that looks like this:
You can now remove the usage of LayoutContainer and make some updates in your view holder:
Here we have simply changed our constructor to take the binding class in parameter instead of having a view object.
To instantiate your view holder, you can create a static method in a companion object:
Includes
In your layouts, you probably have views that are included in your layout using the include tag. Then, you must be aware that it can be tricky to deal with it with view binding.
With a view group
If your included view has a root view that is a ViewGroup (LinearLayout, FrameLayout, etc.) then you can set an id to the include tag in your main layout.
The generated LayoutBinding class will have a property mergeLayout of type LayoutMergeBinding that reference the layout_mege layout views.
So in an activity, you can easily reference your TextView as:
With a
If your included view uses a tag in it like in this example:
And your main layout is:
Then, the layout_merge.xml file content will be merged into your main layout. But, the binding class will not expose a property titleLbl (it will only contain your root LinearLayout ) 😭.
So, what can you do to access to your titleLbl ? You’ll need to re-bind your view with another binding class. As each layout files have its own binding class, your layout_merge layout will have a LayoutMergeBinding class generated. That’s the one we will use to retrieve our views.
From an activity, you can add another property for the 2nd binding class:
The mergeBinding property will then allow you to access the titleLbl view.
Conclusion
We hope this article was useful to you if you’re still refactoring your views to use ViewBinding or to give you some hints to refactor how you’re using it.
And you, how did you implement it into your app? Don’t hesitate to share with us your tip&tricks!
If you want to join our Bureau of Technology or any other Back Market department, take a look here, we’re hiring! 🦄
Источник
Kotlin Android Extensions: полное руководство
Oct 9, 2018 · 7 min read
Если у вас уже есть опыт разработки приложений для Android, вероятно, вы уже устали использовать findViewById, чтобы восстанавливать view. Или, может быть, вы уже сдались и начали использовать известную библиотеку ― Butterknife. Если это ваш случай, то вы влюбитесь в Kotlin Android Extensions.
Что такое Kotlin Android Extensions?
Kotlin Android Extensions — это плагин для Kotlin, который включён в стандартный пакет. Он позволяет восстанавливать view из Activities, Fragments, и Views удивительно-бесшовным способом.
Плагин генерирует дополнительный код, который позволяет получить доступ к view в виде XML, так же, как если бы вы имели дело с properties с именем id, который вы использовали при определении структуры.
Т а кже он создаёт локальный кэш view. При первом использовании свойства, плагин выполнит стандартный findViewById. В последующем, view будет восстановлен из кэша, поэтому доступ к нему будет быстрее.
Как этим пользоваться
Посмотрим, насколько это просто. В первом примере рассмотрим activity:
Включаем Kotlin Android Extensions в наш код
Хотя плагин уже интегрирован (устанавливать новый не нужно), если вы хотите его использовать, вам нужно подключить плагин в модуль Android:
И это все что вам потребуется. Теперь вы можете начать работу с ним.
Восстанавливаем view из XML
С этого момента, восстановить view, также просто, как использовать view id, которое вы определили в XML, непосредственно в вашем activity.
Представьте, что у вас есть такой XML:
Как видно, в TextView есть id welcomeMessage .
Просто напишите следующий код в MainActivity:
Чтобы это работало, нужен специальный импорт (я написал его ниже), но IDE может автоматически импортировать его. Проще и быть не может!
Как я уже упоминал, сгенерированный код включает кэш view, поэтому, если вы повторно обратитесь к view, это не потребует выполнения findViewById снова.
Давайте посмотрим, что происходит под капотом.
Магия Kotlin Android Extensions
Когда начинаешь работать с Kotlin, действительно интересно понять байт-код, который генерируется при использовании той или иной функции. Это поможет вам понять скрытую цену ваших решений.
В меню Tools → Kotlin, вы найдёте мощный инструмент Show Kotlin Bytecode. Если кликнуть по нему, вы увидите байт-код, который будет сгенерирован, когда открытый файл класса будет скомпилирован.
Для большинства людей, байт-код не особенно полезен, но тут есть ещё одна опция: Decompile.
Эта опция покажет вам Java репрезентацию байт-кода, который был сгенерирован в Kotlin. Так, вы сможете более-менее понять, как будет выглядеть код Java, эквивалентный тому, что вы написали на Kotlin.
Я собираюсь использовать это на примере activity и посмотреть код, сгенерированный Kotlin Android Extensions.
Вот интересная часть:
Это и есть кэш view, о котором мы говорили.
При запросе view, его поиск начинается в кэше. Если view нет в кэше, то он туда будет добавлен, как только будет найден. Всё просто.
Кроме того, в код добавляется функция для очистки кэша: clearFindViewByIdCache. Её можно использовать, скажем, когда старые view уже не актуальны и вам нужно получить новые.
Тогда, эта строка:
Превращается в эту:
Таким образом, плагин не генерирует свойства для каждого view, это ненастоящие свойства. Плагин просто заменяет код во время компиляции, чтобы получить доступ к кэшу view. Затем, он приводит к нужному типу и вызывает метод.
Kotlin Android Extensions ― работа с фрагментами
Этот плагин можно использовать с фрагментами.
Проблема с фрагментами заключается в том, что view может обновится, а экземпляр фрагмента останется прежним. Что произойдёт? А то, что view находящееся в кэше, перестанет быть валидным.
Давайте посмотрим код, который генерирует плагин, если мы используем его с фрагментом. Я создаю простой фрагмент, который использует тот же XML, который я написал выше:
В onViewCreated , я снова изменяю текст TextView . Что насчёт сгенерированного байт-кода?
Всё тоже самое, как и в activity, с небольшой разницей:
Когда view перестанет быть актуальным, этот метод вызовет clearFindViewByIdCache , так что мы в безопасности!
Kotlin Android extensions и Custom View
С custom view, плагин работает схожим образом. Скажем, у нас есть такой view:
Я создаю очень простое custom view и генерирую конструкторы с новым намерением, которое использует аннотацию @JvmOverloads :
В примере выше, я изменяю текст в itemTitle . Сгенерированный код должен искать view в кэше. Не имеет смысла снова копировать тот же код полностью. Вы можете наблюдать это в строке, которая изменяет текст:
Отлично! В custom views мы тоже вызываем findViewById только первый раз.
Восстанавливаем views из другого view
Последняя альтернатива, которую предлагает Kotlin Android Extensions: использовать свойства напрямую из другого view.
Я использую схему, схожую с той, что была в предыдущем разделе. Представьте, что это было закачано в адаптер для примера.
Вы также можете получить доступ к subviews напрямую, просто используя этот плагин:
Хотя плагин и может помочь заполнить import, здесь есть небольшое отличие:
Несколько вещей, которые вам нужно знать:
- Во время компиляции, вы можете ссылаться на любое view из любого другого view. Это значит, вы можете ссылаться на view, которое не является его прямым потомком. Это плохо скажется на времени выполнения при попытке восстановить view, которого не существует.
- В этом случае, view не кэшировано, как в случае с Activities и Fragments.
Почему так? В отличие от предыдущих случаев, здесь у плагина нет места, чтобы генерировать необходимый код для кэша.
Если вы снова перечитаете код, который сгенерирован плагином, где вызываются свойства из view, вы увидите это:
Как видно, здесь нет запроса к кэшу. Будьте осторожны, если у вас комплексное view, и вы используете его в адаптере. Это может повлиять на производительность.
Также у вас есть альтернатива: Kotlin 1.1.4
Kotlin Android Extensions версии 1.1.4
Начиная с этой, новой версии Kotlin, в Android Extensions включили новые интересные функции: кэш в любом классе (включая ViewHolder ) и новая аннотация, названная @Parcelize . Также есть способ настраивать сгенерированный кэш.
Мы вернёмся к ним через минуту, но вам следует знать, что эти новые фичи ― не завершены, так что, вам нужно включить их с помощью build.gradle :
Применяем на ViewHolder (или любом пользовательском классе)
Теперь есть простой способ построить кэш для любого класса. Единственное требование заключается в том, чтобы класс имплементировал интерфейс LayoutContainer . Этот интерфейс предоставит view, которое плагин будет использовать, чтобы искать subviews. Представьте, что у вас есть ViewHolder, который содержит view со структурой, описанной в предыдущем примере. Всё что вам нужно сделать:
containerView ― единственное, что мы перезаписываем из интерфейса LayoutContainer . Это все что вам необходимо.
С этого момента, у вас есть доступ к views напрямую. Нет необходимости присоединять itemView, чтобы получить доступ к subviews.
Ещё раз, если вы посмотрите на сгенерированный код, вы увидите, что view берётся из кэша:
Здесь я применял его на ViewHolder , но это достаточно универсальный подход и его можно использовать с любым классом.
Kotlin Android Extension. Реализация Parcelable
С новой аннотацией @Parcelize , можно с лёгкостью имплементировать Parcelable c любым классом.
Вам нужно добавить аннотацию, а плагин сделает за вас всю сложную работу:
Далее, как вам должно быть известно, можно добавить объект к любому намерению:
И восстановить объект из намерения в любой точке (в этом случае: из activity):
Настройка кэша
В этот экспериментальный набор, включена новая фича ― аннотация @ContainerOptions . Она даёт возможность настраивать способ построения кэша, или даже предотвращать его создание классом.
По умолчанию, используется Hashmap , как мы видели ранее. Его можно заменить на SparseArray из Android framework, что может быть более эффективно в некоторых ситуациях. Или, если вам не нужен кэш для класса, по каким-то причинам, такая возможность тоже есть.
Вот как это использовать:
На данный момент существуют следующие варианты:
Заключение
Вы увидели, как легко работать с Android views в Kotlin. Благодаря простому плагину, можно забыть обо всём этом ужасном коде, связанном с восстановлением view. Этот плагин создаёт для нас необходимые свойства с приведением правильного типа, безо всяких проблем.
Кроме того, Kotlin 1.1.4 добавляет интересные фичи, которые будут действительно полезны в некоторых случаях, к которым плагин ранее не имел отношения.
Источник