Пару дней назад 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.
Немного жаль, что такой инструмент не появился несколько лет назад.