Привязка представлений   Часть Android Jetpack.

Привязка представлений – это функция, которая упрощает написание кода, взаимодействующего с представлениями. После включения привязки представлений в модуле генерируется класс привязки для каждого XML-файла макета, присутствующего в этом модуле. Экземпляр класса привязки содержит прямые ссылки на все представления, у которых есть идентификатор в соответствующем макете.

В большинстве случаев привязка представлений заменяет findViewById.

Настроить

Привязка представлений включается отдельно для каждого модуля. Чтобы включить привязку представлений в модуле, задайте для параметра сборки viewBinding значение true в файле build.gradle на уровне модуля, как показано в следующем примере:

Яркий

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

Kotlin

android {
    ...
    buildFeatures {
        viewBinding = true
    }
}

Если вы хотите, чтобы файл макета игнорировался при создании классов привязки, добавьте атрибут tools:viewBindingIgnore="true" в корневое представление этого файла макета:

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

Использование

Если привязка представлений включена для модуля, класс привязки генерируется для каждого XML-файла макета, который содержит модуль. Каждый класс привязки содержит ссылки на корневое представление и все представления, у которых есть идентификатор. Название класса привязки генерируется путем преобразования названия XML-файла в стиль PascalCase и добавления слова 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. В этом классе два поля: TextView с названием name и Button с названием button. У элемента ImageView в макете нет идентификатора, поэтому в классе привязки нет ссылки на него.

Каждый класс привязки также включает метод getRoot(), предоставляющий прямую ссылку на корневое представление соответствующего файла макета. В этом примере метод getRoot() в классе ResultProfileBinding возвращает корневое представление LinearLayout.

В следующих разделах показано, как использовать сгенерированные классы привязки в действиях и фрагментах.

Как использовать привязку представлений в действиях

Чтобы настроить экземпляр класса связывания для использования с действием, выполните следующие шаги в методе onCreate() действия:

  1. Вызовите статический метод inflate(), включенный в сгенерированный класс привязки. Это создаст экземпляр класса привязки, который будет использоваться действием.
  2. Получите ссылку на корневое представление, вызвав метод getRoot() или используя синтаксис свойств Kotlin.
  3. Передайте корневое представление в setContentView(), чтобы сделать его активным на экране.

Эти шаги показаны в следующем примере:

Kotlin

private lateinit var binding: ResultProfileBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}

Java

private ResultProfileBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = ResultProfileBinding.inflate(getLayoutInflater());
    View view = binding.getRoot();
    setContentView(view);
}

Теперь вы можете использовать экземпляр класса привязки для ссылки на любое из представлений:

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

Использование привязки представлений во фрагментах

Чтобы настроить экземпляр класса привязки для использования с фрагментом, выполните следующие действия в методе onCreateView() фрагмента:

  1. Вызовите статический метод inflate(), включенный в сгенерированный класс привязки. Это создаст экземпляр класса привязки для использования фрагментом.
  2. Получите ссылку на корневое представление, вызвав метод getRoot() или используя синтаксис свойств Kotlin.
  3. Верните корневое представление из метода onCreateView(), чтобы сделать его активным представлением на экране.

Kotlin

private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!

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

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

Java

private ResultProfileBinding binding;

@Override
public View onCreateView (LayoutInflater inflater,
                          ViewGroup container,
                          Bundle savedInstanceState) {
    binding = ResultProfileBinding.inflate(inflater, container, false);
    View view = binding.getRoot();
    return view;
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    binding = null;
}

Теперь вы можете использовать экземпляр класса привязки для ссылки на любое из представлений:

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

Подсказки для разных конфигураций

При объявлении представлений в нескольких конфигурациях иногда имеет смысл использовать разные типы представлений в зависимости от конкретного макета. Ниже приведен пример фрагмента кода:

# in res/layout/example.xml

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" />

В этом случае можно ожидать, что сгенерированный класс будет содержать поле userBio типа TextView, поскольку TextView – это общий базовый класс. Из-за технических ограничений генератор кода привязки представлений не может определить это и вместо этого генерирует поле View. Для этого нужно будет преобразовать поле с помощью binding.userBio as TextView.

Чтобы обойти это ограничение, привязка представлений поддерживает атрибут tools:viewBindingType, позволяющий указать компилятору, какой тип использовать в сгенерированном коде. В предыдущем примере вы можете использовать этот атрибут, чтобы компилятор сгенерировал поле как TextView:

# in res/layout/example.xml (unchanged)

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" tools:viewBindingType="TextView" />

Рассмотрим другой пример. Предположим, у вас есть два макета: один содержит BottomNavigationView, а другой – NavigationRailView. Оба класса расширяют NavigationBarView, который содержит большую часть деталей реализации. Если в коде не нужно точно знать, какой подкласс присутствует в текущем макете, вы можете использовать tools:viewBindingType, чтобы задать сгенерированный тип NavigationBarView в обоих макетах:

# in res/layout/navigation_example.xml

<BottomNavigationView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

# in res/layout-w720/navigation_example.xml

<NavigationRailView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

Привязка представления не может проверить значение этого атрибута при создании кода. Чтобы избежать ошибок компиляции и выполнения, значение должно соответствовать следующим условиям:

  • Значение должно быть классом, унаследованным от android.view.View.
  • Значение должно быть надклассом тега, в котором оно размещено. Например, следующие значения не будут работать:

      <TextView tools:viewBindingType="ImageView" /> <!-- ImageView is not related to TextView. -->
      <TextView tools:viewBindingType="Button" /> <!-- Button is not a superclass of TextView. -->
    
  • Итоговый тип должен быть одинаковым во всех конфигурациях.

Отличия от findViewById

У привязки представлений есть важные преимущества по сравнению с использованием findViewById:

  • Безопасность нулевых значений. Поскольку привязка представлений создает прямые ссылки на представления, нет риска исключения нулевого указателя из-за недопустимого идентификатора представления. Кроме того, если представление присутствует только в некоторых конфигурациях макета, поле, содержащее ссылку на него в классе привязки, помечается как @Nullable.
  • Типобезопасность. Поля в каждом классе привязки имеют типы, соответствующие представлениям, на которые они ссылаются в XML-файле. Это означает, что нет риска исключения приведения классов.

Эти различия означают, что несовместимость между макетом и кодом приводит к сбою сборки во время компиляции, а не во время выполнения.

Сравнение с привязкой данных

Привязка представлений и привязка данных генерируют классы привязки, которые можно использовать для прямой ссылки на представления. Однако привязка представлений предназначена для более простых сценариев использования и имеет следующие преимущества по сравнению с привязкой данных:

  • Более быстрая компиляция. Привязке представлений не требуется обработка аннотаций, поэтому компиляция выполняется быстрее.
  • Простота использования. Привязка представлений не требует специально помеченных XML-файлов макета, поэтому ее быстрее внедрить в приложения. После включения привязки представлений в модуле она автоматически применяется ко всем макетам этого модуля.

С другой стороны, привязка представления имеет следующие ограничения по сравнению с привязкой данных:

Поэтому в некоторых случаях лучше использовать в проекте и привязку представлений, и привязку данных. Вы можете использовать привязку данных в макетах, требующих расширенных функций, и привязку представлений в макетах, которые этого не делают.

Дополнительные ресурсы

Чтобы узнать больше о привязке представлений, ознакомьтесь со следующими дополнительными ресурсами:

Блоги

Видео