Урок на Kotlin: ViewBinding в Android Studio

Каждый, кто начинает писать под Android, быстро знакомится с findViewById. Код с ним работает, но выглядит громоздко, а при ошибке в названии id приложение падает. Сегодня на занятии в Android Studio мы освоим более удобный инструмент — ViewBinding. Он автоматически создаёт ссылки на все элементы разметки, и вы обращаетесь к ним через одну переменную, не боясь опечаток. Урок построен на языке Kotlin и рассчитан на тех, кто уже пробовал что-то создавать, но хочет писать чище и безопаснее.


Чем ViewBinding лучше findViewById

При использовании findViewById вы вручную ищете каждый элемент по id и приводите его к нужному типу. Если ошибиться в id, компилятор не заметит, и ошибка проявится только при запуске. ViewBinding же генерирует для каждого XML-файла отдельный класс, в котором уже есть все View с правильными типами. Обращение к binding.myButton даёт сразу Button, а не View, и никаких приведений не требуется. Плюс null-безопасность и проверка на этапе компиляции делают код надёжным.


Как включить ViewBinding в проекте

Никакие библиотеки добавлять не нужно — ViewBinding встроен в Android Gradle Plugin. Откройте build.gradle.kts уровня модуля app и внутри блока android добавьте пару строк. После синхронизации Gradle генерация классов включится для всех XML-файлов разметки.

android {
    // ... compileSdk и прочее
    buildFeatures {
        viewBinding = true
    }
}

Если для какого-то конкретного макета ViewBinding не нужен (например, он редко обновляется), можно отключить его прямо в XML-файле, добавив атрибут tools:viewBindingIgnore="true" в корневой элемент.


Переписываем Activity на ViewBinding

Допустим, у нас есть простой экран с текстовой меткой и кнопкой, как в самом первом занятии. Раньше мы находили их через findViewById. Теперь вместо этого в onCreate нужно объявить переменную binding и использовать её.

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.myText.text = "Привет, ViewBinding!"
        binding.myButton.setOnClickListener {
            binding.myText.text = "Кнопка нажата"
        }
    }
}

Класс ActivityMainBinding сгенерирован автоматически из имени файла activity_main.xml путём преобразования к UpperCamelCase и добавления суффикса Binding. Все id из разметки превратились в поля этого класса.


ViewBinding во Fragment

Для фрагментов подход немного отличается, потому что нужно вовремя очищать ссылку, чтобы избежать утечек памяти. Создадим фрагмент с тем же макетом и правильно настроим binding.

class MyFragment : Fragment() {
    private var _binding: FragmentMyBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentMyBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.myText.text = "Фрагмент с ViewBinding"
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

Переменная _binding обнуляется в onDestroyView — это важно, потому что View фрагмента может быть уничтожен раньше самого фрагмента, а ссылка на него будет держать память. Код аккуратный и безопасный.


ViewBinding в адаптере RecyclerView

Адаптер тоже можно переписать на ViewBinding, чтобы не вызывать findViewById в ViewHolder. Вместо этого ViewHolder принимает объект binding, и доступ к элементам идёт напрямую.

class FruitAdapter(
    private val fruitList: List<Fruit>,
    private val onItemClick: (Fruit) -> Unit
) : RecyclerView.Adapter<FruitAdapter.FruitViewHolder>() {

    class FruitViewHolder(val binding: ItemFruitBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {
        val binding = ItemFruitBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return FruitViewHolder(binding)
    }

    override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
        val fruit = fruitList[position]
        holder.binding.tvName.text = fruit.name
        holder.binding.tvDescription.text = fruit.description
        holder.binding.root.setOnClickListener { onItemClick(fruit) }
    }

    override fun getItemCount(): Int = fruitList.size
}

Советы для начинающих

  • Всегда обнуляйте _binding во Fragment. Это предотвращает утечки памяти и является рекомендацией Google.
  • Используйте lateinit в Activity. Переменная binding инициализируется в onCreate и живёт до уничтожения Activity, поэтому lateinit здесь полностью оправдан.
  • Не забывайте, что binding.root — это корневой View. Именно его нужно передавать в setContentView или возвращать из onCreateView.

Что мы освоили

Этот урок в Android Studio на языке Kotlin показал, как отказаться от findViewById и писать более чистый код с помощью ViewBinding. Вы включили генерацию классов, переписали Activity и Fragment, а также адаптер RecyclerView. Теперь обращение к кнопкам и тексту стало лаконичным и безопасным. Попробуйте прямо сейчас перевести свой проект на ViewBinding — вы увидите, насколько меньше стало кода и как легко его читать.