Android Kotlin Fundamentals 02.4: основы привязки данных

Эта кодовая лаборатория является частью курса Android Kotlin Fundamentals. Вы получите максимальную отдачу от этого курса, если будете последовательно работать с лабораториями кода. Все кодовые лаборатории курса перечислены на целевой странице кодовых лабораторий Android Kotlin Fundamentals .

Введение

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

Чтобы установить данные в представлениях, вы использовали строковые ресурсы и установили данные из действия. Было бы более эффективно, если бы представление знало о данных. И, к счастью, это снова возможно.

В этой лабораторной работе вы узнаете, как использовать привязку данных, чтобы устранить необходимость в findViewById() . Вы также узнаете, как использовать привязку данных для доступа к данным непосредственно из представления.

Что вы уже должны знать

Вы должны быть знакомы с:

  • Что такое активность и как настроить активность с макетом в onCreate() .
  • Создание текстового представления и настройка текста, отображаемого в текстовом представлении.
  • Использование findViewById() для получения ссылки на представление.
  • Создание и редактирование базового макета XML для представления.

Что вы узнаете

  • Как использовать библиотеку привязки данных для устранения неэффективных вызовов findViewById() .
  • Как получить доступ к данным приложения напрямую из XML.

Что ты будешь делать

  • Измените приложение, чтобы использовать привязку данных вместо findViewById() и получать доступ к данным непосредственно из XML-файлов макета.

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

Вот что делает приложение AboutMe:

  • Когда пользователь открывает приложение, оно показывает имя, поле для ввода псевдонима, кнопку « Готово », звездочку и прокручиваемый текст.
  • Пользователь может ввести псевдоним и нажать кнопку « Готово ». Редактируемое поле и кнопка заменяются текстовым представлением, которое показывает введенный псевдоним.


Вы можете использовать код, созданный в предыдущей лаборатории кода, или загрузить код AboutMeDataBinding-Starter с GitHub.

Код, который вы написали в предыдущих лабораторных работах, использует функцию findViewById() для получения ссылок на представления.

Каждый раз, когда вы используете findViewById() для поиска представления после его создания или повторного создания, система Android просматривает иерархию представлений во время выполнения, чтобы найти его. Если у вашего приложения всего несколько просмотров, это не проблема. Однако рабочие приложения могут иметь десятки представлений в макете, и даже при самом лучшем дизайне будут вложенные представления.

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

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

Привязка данных имеет следующие преимущества:

  • Код короче, его легче читать и легче поддерживать, чем код, использующий findByView() .
  • Данные и представления четко разделены. Это преимущество привязки данных становится все более важным позже в этом курсе.
  • Система Android проходит иерархию представлений только один раз, чтобы получить каждое представление, и это происходит во время запуска приложения, а не во время выполнения, когда пользователь взаимодействует с приложением.
  • Вы получаете безопасность типов для доступа к представлениям. ( Безопасность типов означает, что компилятор проверяет типы во время компиляции и выдает ошибку, если вы пытаетесь присвоить переменной неправильный тип.)

В этой задаче вы настраиваете привязку данных и используете привязку данных для замены вызовов findViewById() вызовами объекта привязки.

Шаг 1. Включите привязку данных

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

  1. Если у вас нет приложения AboutMe из предыдущей кодовой лаборатории, получите код AboutMeDataBinding-Starter на GitHub. Откройте его в Android Studio.
  2. Откройте build.gradle (Module: app) .
  3. Внутри раздела android перед закрывающей скобкой добавьте раздел dataBinding и установите для параметра enabled значение true .
dataBinding {
    enabled = true
}
  1. При появлении запроса синхронизируйте проект. Если вас не попросят, выберите «Файл» > «Синхронизировать проект с файлами Gradle» .
  2. Вы можете запустить приложение, но вы не увидите никаких изменений.

Шаг 2. Измените файл макета, чтобы его можно было использовать с привязкой данных

Для работы с привязкой данных вам необходимо обернуть XML-макет тегом <layout> . Это связано с тем, что корневой класс больше не является группой представлений, а представляет собой макет, содержащий группы представлений и представления. Затем объект привязки может знать о макете и представлениях в нем.

  1. Откройте файл activity_main.xml .
  2. Переключитесь на вкладку « Текст ».
  3. Добавьте <layout></layout> в качестве самого внешнего тега вокруг <LinearLayout> .
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Выберите « Код» > «Переформатировать код» , чтобы исправить отступ кода.

    Объявления пространств имен для макета должны находиться в самом внешнем теге.
  1. Вырежьте объявления пространств имен из <LinearLayout> и вставьте их в <layout> . Ваш открывающий <layout> должен выглядеть так, как показано ниже, а <LinearLayout> должен содержать только свойства представления.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. Создайте и запустите приложение, чтобы убедиться, что вы сделали это правильно.

Шаг 3: Создайте объект привязки в основном действии

Добавьте ссылку на объект привязки в основное действие, чтобы вы могли использовать его для доступа к представлениям:

  1. Откройте файл MainActivity.kt .
  2. Перед onCreate() на верхнем уровне создайте переменную для объекта привязки. Эта переменная обычно называется binding .

    Тип binding , класс ActivityMainBinding , создается компилятором специально для этого основного действия. Имя происходит от имени файла макета, то есть activity_main + Binding .
private lateinit var binding: ActivityMainBinding
  1. По запросу Android Studio импортируйте ActivityMainBinding . Если вас не попросят, нажмите ActivityMainBinding и нажмите Alt+Enter ( Option+Enter на Mac), чтобы импортировать этот отсутствующий класс. (Дополнительные сочетания клавиш см. в разделе Сочетания клавиш .)

    Оператор import должен выглядеть примерно так, как показано ниже.
import com.example.android.aboutme.databinding.ActivityMainBinding

Затем вы заменяете текущую setContentView() инструкцией, которая делает следующее:

  • Создает объект привязки.
  • Использует setContentView() из класса DataBindingUtil , чтобы связать макет activity_main с MainActivity . Эта setContentView() также выполняет некоторые настройки привязки данных для представлений.
  1. В onCreate() замените setContentView() следующей строкой кода.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. Импортируйте DataBindingUtil .
import androidx.databinding.DataBindingUtil

Шаг 4: Используйте объект привязки для замены всех вызовов findViewById()

Теперь вы можете заменить все вызовы findViewById() ссылками на представления, находящиеся в объекте привязки. Когда объект привязки создан, компилятор генерирует имена представлений в объекте привязки из идентификаторов представлений в макете, преобразовывая их в верблюжий регистр. Так, например, done_button становится doneButton в объекте привязки, nickname_edit становится nicknameEdit , а nickname_text становится nicknameText .

  1. В onCreate() замените код, использующий findViewById() для поиска done_button , кодом, который ссылается на кнопку в объекте привязки.

    Замените этот код: findViewById<Button>(R.id. done_button )
    с помощью: binding.doneButton

    Ваш готовый код для установки прослушивателя кликов в onCreate() должен выглядеть так.
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. Сделайте то же самое для всех вызовов findViewById() в функции addNickname() .
    Замените все вхождения findViewById< View >(R.id. id_view ) binding. idView . Сделайте это следующим образом:
  • Удалите определения для переменных editText и nicknameTextView вместе с их вызовами findViewById() . Это даст вам ошибки.
  • Исправьте ошибки, получив представления nicknameText , nicknameEdit и doneButton из объекта binding вместо (удаленных) переменных.
  • Замените view.visibility на binding.doneButton.visibility . Использование binding.doneButton вместо переданного view делает код более согласованным.

    В результате получается следующий код:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • В функционале изменений нет. При желании теперь вы можете удалить параметр view и обновить все виды использования view , чтобы использовать binding.doneButton внутри этой функции.
  1. Для nicknameText требуется String , а для nicknameEdit.text требуется Editable . При использовании привязки данных необходимо явно преобразовать Editable в String .
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Вы можете удалить выделенный серым цветом импорт.
  2. Котлинизируйте функцию с помощью apply{} .
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Создайте и запустите свое приложение... и оно должно выглядеть и работать точно так же, как и раньше.

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

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

Шаг 1. Создайте класс данных MyName.

  1. В Android Studio в каталоге java откройте файл MyName.kt . Если у вас нет этого файла, создайте новый файл Kotlin и назовите его MyName.kt .
  2. Определите класс данных для имени и псевдонима. Используйте пустые строки в качестве значений по умолчанию.
data class MyName(var name: String = "", var nickname: String = "")

Шаг 2. Добавьте данные в макет

В файле activity_main.xml имя в настоящее время задается в TextView из строкового ресурса. Вам нужно заменить ссылку на имя ссылкой на данные в классе данных.

  1. Откройте файл activity_main.xml на вкладке « Текст ».
  2. В верхней части макета между тегами <layout> и <LinearLayout> вставьте <data></data> . Здесь вы будете соединять представление с данными.
<data>
  
</data>

Внутри тегов данных вы можете объявить именованные переменные, содержащие ссылку на класс.

  1. Внутри <data> добавьте тег <variable> .
  2. Добавьте параметр name , чтобы дать переменной имя "myName" . Добавьте параметр type и задайте для типа полное имя класса данных MyName (имя пакета + имя переменной).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

Теперь вместо использования строкового ресурса для имени вы можете ссылаться на переменную myName .

  1. Замените android:text="@string/name" приведенным ниже кодом.

@={} — это директива для получения данных, на которые ссылаются фигурные скобки.

myName ссылается на ранее определенную вами переменную myName , которая указывает на класс данных myName и извлекает свойство name из класса.

android:text="@={myName.name}"

Шаг 3: Создайте данные

Теперь у вас есть ссылка на данные в файле макета. Затем вы создаете фактические данные.

  1. Откройте файл MainActivity.kt .
  2. Над onCreate() создайте приватную переменную, также называемую myName по соглашению. Назначьте переменной экземпляр класса данных MyName , передав имя.
private val myName: MyName = MyName("Aleks Haecky")
  1. В onCreate() установите значение переменной myName в файле макета на значение только что объявленной переменной myName . Вы не можете получить доступ к переменной в XML напрямую. Вам нужно получить к нему доступ через объект привязки.
binding.myName = myName
  1. Это может показать ошибку, потому что вам нужно обновить объект привязки после внесения изменений. Создайте свое приложение, и ошибка должна исчезнуть.

Шаг 4: Используйте класс данных для псевдонима в TextView

Последний шаг — также использовать класс данных для псевдонима в TextView .

  1. Откройте файл activity_main.xml .
  2. В текстовом представлении nickname_text добавьте свойство text . Ссылка на nickname в классе данных, как показано ниже.
android:text="@={myName.nickname}"
  1. В ActivityMain замените
    nicknameText.text = nicknameEdit.text.toString()
    с кодом для установки псевдонима в переменной myName .
myName?.nickname = nicknameEdit.text.toString()

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

  1. Добавьте invalidateAll() после установки псевдонима, чтобы пользовательский интерфейс обновлялся со значением в обновленном объекте привязки.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Создайте и запустите свое приложение, и оно должно работать точно так же, как и раньше.

Проект Android Studio: AboutMeDataBinding

Шаги по использованию привязки данных для замены вызовов findViewById() :

  1. Включите привязку данных в разделе android файла build.gradle :
    dataBinding { enabled = true }
  2. Используйте <layout> в качестве корневого представления в макете XML.
  3. Определите переменную привязки:
    private lateinit var binding: ActivityMainBinding
  4. Создайте объект привязки в MainActivity , заменив setContentView :
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Замените вызовы findViewById() ссылками на представление в объекте привязки. Например:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneBu binding.doneButton
    (В этом примере имя представления генерируется в верблюжьем регистре из id представления в XML.)

Шаги для привязки представлений к данным:

  1. Создайте класс данных для ваших данных.
  2. Добавьте блок <data> внутрь <layout> .
  3. Определите <variable> с именем и типом, который является классом данных.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. В MainActivity создайте переменную с экземпляром класса данных. Например:
    private val myName: MyName = MyName("Aleks Haecky")
  1. В объекте привязки установите переменную в только что созданную переменную:
    binding.myName = myName
  1. В XML задайте для содержимого представления переменную, которую вы определили в блоке <data> . Используйте запись через точку для доступа к данным внутри класса данных.
    android:text="@={myName.name}"

Удасити курс:

Документация для разработчиков Android:

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

  • При необходимости задайте домашнее задание.
  • Объясните учащимся, как сдавать домашние задания.
  • Оценивайте домашние задания.

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

Если вы работаете с этой кодовой лабораторией самостоятельно, не стесняйтесь использовать эти домашние задания, чтобы проверить свои знания.

Ответьте на эти вопросы

Вопрос 1

Почему вы хотите свести к минимуму явные и неявные вызовы findViewById() ?

  • Каждый раз, когда вызывается findViewById() , он проходит через иерархию представлений.
  • findViewById() работает в основном потоке или потоке пользовательского интерфейса.
  • Эти вызовы могут замедлить работу пользовательского интерфейса.
  • Ваше приложение с меньшей вероятностью выйдет из строя.

вопрос 2

Как бы вы описали привязку данных?

Например, вот что можно сказать о привязке данных:

  • Основная идея привязки данных заключается в создании объекта, который соединяет/сопоставляет/связывает две части удаленной информации вместе во время компиляции, чтобы вам не приходилось искать данные во время выполнения.
  • Объект, который отображает эти привязки для вас, называется объектом привязки .
  • Объект привязки создается компилятором.

Вопрос 3

Что из следующего НЕ является преимуществом привязки данных?

  • Код короче, его легче читать и легче поддерживать.
  • Данные и представления четко разделены.
  • Система Android проходит иерархию представлений только один раз, чтобы получить каждое представление.
  • Вызов findViewById() генерирует ошибку компилятора.
  • Введите безопасность для доступа к представлениям.

Вопрос 4

Какова функция <layout> ?

  • Вы оборачиваете его вокруг своего корневого представления в макете.
  • Привязки создаются для всех представлений в макете.
  • Он обозначает представление верхнего уровня в макете XML, использующем привязку данных.
  • Вы можете использовать <data> внутри <layout> для привязки переменной к классу данных.

Вопрос 5

Как правильно ссылаться на связанные данные в макете XML?

  • android:text="@={myDataClass.property}"
  • android:text="@={myDataClass}"
  • android:text="@={myDataClass.property.toString()}"
  • android:text="@={myDataClass.bound_data.property}"

Начать следующий урок: 3.1: Создать фрагмент

Ссылки на другие лаборатории кода в этом курсе см. на целевой странице лаборатории кода Android Kotlin Fundamentals .