Долгожданный View Binding в Android
Недавно Google выпустил Android Studio 3.6 Canary 11, главным нововведением в которой стал View Binding, о котором было рассказано еще в мае на Google I/O 2019.
View Binding — это инструмент, который позволяет проще писать код для взаимодействия с view. При включении View Binding в определенном модуле он генерирует binding классы для каждого файла разметки (layout) в модуле. Объект сгенерированного binding класса содержит ссылки на все view из файла разметки, для которых указан android:id
.
Как включить
Чтобы включить View Binding в модуле, надо добавить элемент в файл build.gradle
:
android { ... viewBinding { enabled = true } }
Также можно указать, что для определенного файла разметки генерировать binding класс не надо. Для этого надо указать аттрибут tools:viewBindingIgnore="true"
в корневой view в нужном файле разметки.
Как использовать
Каждый сгенерированный binding класс содержит ссылку на корневой view разметки (root
) и ссылки на все view, которые имеют id. Имя генерируемого класса формируется как "название файла разметки", переведенное в camel case + "Binding".
Например, для файла разметки result_profile.xml
:
<LinearLayout ...> <TextView android:id="@+id/name" /> <ImageView android:cropToPadding="true" /> <Button android:id="@+id/button" android:background="@drawable/rounded_button" /> </LinearLayout>
Будет сгенерирован класс ResultProfileBinding
, содержащий 2 поля: TextView name
и Button button
. Для ImageView
ничего сгенерировано не будет, как как оно не имеет id
. Также в классе ResultProfileBinding
будет метод getRoot()
, возвращающий корневой LinearLayout
.
Чтобы создать объект класса ResultProfileBinding
, надо вызвать статический метод inflate()
. После этого можно использовать корневой view как content view
в Activity
:
private lateinit var binding: ResultProfileBinding @Override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) binding = ResultProfileBinding.inflate(layoutInflater) setContentView(binding.root) }
Позже binding
можно использовать для получения view:
binding.name.text = viewModel.name binding.button.setOnClickListener { viewModel.userClicked() }
Отличия от других подходов
Главные преимущества View Binding — это Null safety и Type safety.
При этом если какая-то view имеется в одной конфигурации разметки, но отсутствует в другой (layout-land
, например), то для нее в binding классе будет сгенерировано @Nullable
поле.
Также, если в разных конфигурациях разметки имеются view с одинаковыми id, но разными типами, то для них будет сгенерировано поле с типом android.view.View
. (По крайней мере, в версии 3.6 Canary 11)
А вообще, было бы удобно, если бы сгенерированное поле имело наиболее возможный специфичный тип. Например, чтобы для Button
в одной конфигурации и TextView
в другой генерировалось поле с типом TextView
(т.к. public class Button extends TextView
).
При использовании View Binding все несоответствия между разметкой и кодом будут выявляться на этапе компиляции, что позволит избежать ненужных ошибок во время работы приложения.
Использование в RecyclerView.ViewHolder
Ничего не мешает использовать View Binding при создании view
для RecyclerView.ViewHolder
:
class PersonViewHolder( private val itemPersonBinding: ItemPersonBinding ) : RecyclerView.ViewHolder(itemPersonBinding.root) { fun bind(person: Person) { itemPersonBinding.name.text = person.name } }
Однако, для создания такого ViewHolder
придется написать немного бойлерплейта:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PersonViewHolder { val layoutInflater = LayoutInflater.from(parent.context) val itemPersonBinding = ItemPersonBinding.inflate(layoutInflater, parent, false) return PersonViewHolder(itemPersonBinding) }
Было бы удобнее, если бы при работе с RecyclerView.ViewHolder
метод inflate(...)
не имел параметр layoutInflater
, а сам получал его из передаваемого parent
.
Тут нужно ещё упомянуть, что при использовании View Binding поиск view
через findViewById()
производится только один раз при вызове метода inflate()
. Это дает преимущество над kotlin-android-extensions
, в котором кеширование view
по умолчанию работало только в Activity
и Fragment
, а для RecyclerView.ViewHolder
требовалась дополнительная настройка.
В целом, View Binding – это очень удобная вещь, которую легко начать использовать в существующих проектах. Создатель Butter Knife уже рекомендует переключаться на View Binding.
Немного жаль, что такой инструмент не появился несколько лет назад.
Источник: Долгожданный View Binding в Android