Эта кодовая лаборатория является частью курса Android Kotlin Fundamentals. Вы получите максимальную отдачу от этого курса, если будете последовательно работать с лабораториями кода. Все кодовые лаборатории курса перечислены на целевой странице кодовых лабораторий Android Kotlin Fundamentals .
Введение
В предыдущих лабораторных работах этого урока вы улучшили код для приложения GuessTheWord. Приложение теперь использует объекты ViewModel
, поэтому данные приложения сохраняются после изменений конфигурации устройства, таких как повороты экрана и изменения доступности клавиатуры. Вы также добавили наблюдаемые LiveData
, чтобы представления автоматически уведомлялись об изменении наблюдаемых данных.
В этой кодовой лаборатории вы продолжаете работать с приложением GuessTheWord. Вы привязываете представления к классам ViewModel
в приложении, чтобы представления в вашем макете напрямую взаимодействовали с объектами ViewModel
. (До сих пор в вашем приложении представления взаимодействовали с ViewModel
косвенно , посредством фрагментов приложения.) После интеграции привязки данных с объектами ViewModel
вам больше не нужны обработчики кликов во фрагментах приложения, поэтому вы их удаляете.
Вы также изменяете приложение GuessTheWord, чтобы использовать LiveData
в качестве источника привязки данных для уведомления пользовательского интерфейса об изменениях в данных без использования методов наблюдателя LiveData
.
Что вы уже должны знать
- Как создавать базовые приложения для Android на Kotlin.
- Как работают жизненные циклы активности и фрагментов.
- Как использовать объекты
ViewModel
в вашем приложении. - Как хранить данные с помощью
LiveData
вViewModel
. - Как добавить методы наблюдателя для наблюдения за изменениями в данных
LiveData
.
Что вы узнаете
- Как использовать элементы библиотеки привязки данных .
- Как интегрировать
ViewModel
с привязкой данных. - Как интегрировать
LiveData
с привязкой данных. - Как использовать привязки слушателей для замены прослушивателей кликов во фрагменте.
- Как добавить форматирование строк в выражения привязки данных.
Что ты будешь делать
- Представления в макетах GuessTheWord косвенно взаимодействуют с объектами
ViewModel
, используя контроллеры пользовательского интерфейса (фрагменты) для передачи информации. В этой лаборатории кода вы привязываете представления приложения к объектамViewModel
, чтобы представления напрямую взаимодействовали с объектамиViewModel
. - Вы изменяете приложение, чтобы использовать
LiveData
в качестве источника привязки данных. После этого изменения объектыLiveData
уведомляют пользовательский интерфейс об изменениях в данных, и методы наблюдателяLiveData
больше не нужны.
В лабораториях кода Урока 5 вы разрабатываете приложение GuessTheWord, начиная с начального кода. GuessTheWord — это игра в стиле шарад для двух игроков, в которой игроки сотрудничают, чтобы набрать как можно больше очков.
Первый игрок смотрит на слова в приложении и разыгрывает каждое из них по очереди, стараясь не показывать слово второму игроку. Второй игрок пытается угадать слово.
Чтобы начать игру, первый игрок открывает приложение на устройстве и видит слово, например «гитара», как показано на снимке экрана ниже.
Первый игрок разыгрывает слово, стараясь не произносить само слово.
- Когда второй игрок угадывает слово правильно, первый игрок нажимает кнопку « Понял », которая увеличивает счет на единицу и показывает следующее слово.
- Если второй игрок не может угадать слово, первый игрок нажимает кнопку « Пропустить », которая уменьшает счет на единицу и переходит к следующему слову.
- Чтобы завершить игру, нажмите кнопку End Game . (Эта функциональность отсутствует в начальном коде для первой кодовой лаборатории в серии.)
В этой лаборатории кода вы улучшите приложение GuessTheWord, интегрировав привязку данных с LiveData
в объекты ViewModel
. Это автоматизирует связь между представлениями в макете и объектами ViewModel
и позволяет упростить код с помощью LiveData
.
Титульный экран | Экран игры | Экран счета |
В этой задаче вы находите и запускаете начальный код для этой лаборатории кода. Вы можете использовать приложение GuessTheWord, созданное в предыдущей кодовой лаборатории, в качестве начального кода или скачать начальное приложение.
- (Необязательно) Если вы не используете код из предыдущей лаборатории кода, загрузите начальный код для этой лаборатории кода. Разархивируйте код и откройте проект в Android Studio.
- Запустите приложение и играйте.
- Обратите внимание, что кнопка « Понял » показывает следующее слово и увеличивает оценку на единицу, а кнопка « Пропустить » отображает следующее слово и уменьшает оценку на единицу. Кнопка End Game завершает игру.
- Прокрутите все слова и обратите внимание, что приложение автоматически переходит к экрану оценки.
В предыдущей кодовой лаборатории вы использовали привязку данных как типобезопасный способ доступа к представлениям в приложении GuessTheWord. Но реальная сила привязки данных заключается в выполнении того, что следует из названия: привязка данных непосредственно к объектам представления в вашем приложении.
Текущая архитектура приложения
В вашем приложении представления определяются в макете XML, а данные для этих представлений хранятся в объектах ViewModel
. Между каждым представлением и соответствующей ему ViewModel
находится контроллер пользовательского интерфейса, который действует как реле между ними.
Например:
- Кнопка Got It определяется как представление
Button
в файле макетаgame_fragment.xml
. - Когда пользователь нажимает кнопку Got It , прослушиватель кликов во фрагменте
GameFragment
вызывает соответствующий прослушиватель кликов вGameViewModel
. - Счет обновляется в
GameViewModel
.
Представление Button
и GameViewModel
не взаимодействуют напрямую — им нужен прослушиватель кликов, который находится в GameFragment
.
ViewModel передается в привязку данных
Было бы проще, если бы представления в макете взаимодействовали напрямую с данными в объектах ViewModel
, не полагаясь на контроллеры пользовательского интерфейса в качестве посредников.
Объекты ViewModel
содержат все данные пользовательского интерфейса в приложении GuessTheWord. Передавая объекты ViewModel
в привязку данных, вы можете автоматизировать часть связи между представлениями и объектами ViewModel
.
В этой задаче вы связываете GameViewModel
и ScoreViewModel
с соответствующими XML-макетами. Вы также настраиваете привязки прослушивателя для обработки событий кликов.
Шаг 1. Добавьте привязку данных для GameViewModel.
На этом шаге вы связываете GameViewModel
с соответствующим файлом макета game_fragment.xml
.
- В файле
game_fragment.xml
добавьте переменную привязки данных типаGameViewModel
. Если у вас есть ошибки в Android Studio, очистите и пересоберите проект.
<layout ...>
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
<androidx.constraintlayout...
- В файле
GameFragment
передайтеGameViewModel
в привязку данных.
Для этого присвойтеviewModel
переменнойbinding.gameViewModel
, которую вы объявили на предыдущем шаге. Поместите этот код внутрьonCreateView()
после инициализацииviewModel
представления. Если у вас есть ошибки в Android Studio, очистите и пересоберите проект.
// Set the viewmodel for databinding - this allows the bound layout access
// to all the data in the ViewModel
binding.gameViewModel = viewModel
Шаг 2. Используйте привязки прослушивателя для обработки событий
Привязки прослушивателя — это выражения привязки, которые запускаются при запуске таких событий, как onClick()
, onZoomIn()
или onZoomOut()
. Привязки прослушивателя записываются как лямбда-выражения.
Привязка данных создает прослушиватель и устанавливает его в представлении. Когда происходит прослушиваемое событие, слушатель оценивает лямбда-выражение. Привязки прослушивателя работают с подключаемым модулем Android Gradle версии 2.0 или выше. Чтобы узнать больше, прочтите Макеты и выражения привязки .
На этом шаге вы замените прослушиватели кликов в GameFragment
привязками прослушивателей в файле game_fragment.xml
.
- В
game_fragment.xml
добавьте атрибутonClick
кskip_button
. Определите выражение привязки и вызовите методonSkip()
вGameViewModel
. Это выражение привязки называется привязкой слушателя .
<Button
android:id="@+id/skip_button"
...
android:onClick="@{() -> gameViewModel.onSkip()}"
... />
- Точно так же привяжите событие
correct_button
кonCorrect
()
вGameViewModel
.
<Button
android:id="@+id/correct_button"
...
android:onClick="@{() -> gameViewModel.onCorrect()}"
... />
- Привяжите событие click
end_game_button
кonGameFinish
()
вGameViewModel
.
<Button
android:id="@+id/end_game_button"
...
android:onClick="@{() -> gameViewModel.onGameFinish()}"
... />
- В
GameFragment
удалите операторы, устанавливающие прослушиватели кликов, и удалите функции, которые вызывают прослушиватели кликов. Они вам больше не нужны.
Код для удаления:
binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
binding.endGameButton.setOnClickListener { onEndGame() }
/** Methods for buttons presses **/
private fun onSkip() {
viewModel.onSkip()
}
private fun onCorrect() {
viewModel.onCorrect()
}
private fun onEndGame() {
gameFinished()
}
Шаг 3. Добавьте привязку данных для ScoreViewModel.
На этом шаге вы связываете ScoreViewModel
с соответствующим файлом макета score_fragment.xml
.
- В файл
score_fragment.xml
добавьте переменную привязки типаScoreViewModel
. Этот шаг аналогичен тому, что вы сделали дляGameViewModel
выше.
<layout ...>
<data>
<variable
name="scoreViewModel"
type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
- В
score_fragment.xml
добавьте атрибутonClick
кplay_again_button
. Определите привязку слушателя и вызовите методonPlayAgain()
вScoreViewModel
.
<Button
android:id="@+id/play_again_button"
...
android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
... />
- В
ScoreFragment
внутриonCreateView()
инициализируйтеviewModel
. Затем инициализируйте переменную привязкиbinding.scoreViewModel
.
viewModel = ...
binding.scoreViewModel = viewModel
- В
ScoreFragment
удалите код, устанавливающий прослушиватель кликов дляplayAgainButton
. Если Android Studio показывает ошибку, очистите и перестройте проект.
Код для удаления:
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Запустите свое приложение. Приложение должно работать, как и раньше, но теперь представления кнопок напрямую взаимодействуют с объектами
ViewModel
. Представления больше не взаимодействуют через обработчики нажатия кнопок вScoreFragment
.
Устранение неполадок с сообщениями об ошибках привязки данных
Когда приложение использует привязку данных, процесс компиляции создает промежуточные классы, используемые для привязки данных. В приложении могут быть ошибки, которые Android Studio не обнаружит, пока вы не попытаетесь скомпилировать приложение, поэтому вы не увидите предупреждений или красного кода во время написания кода. Но во время компиляции вы получаете загадочные ошибки, которые исходят от сгенерированных промежуточных классов.
Если вы получаете загадочное сообщение об ошибке:
- Внимательно посмотрите на сообщение на панели Android Studio Build . Если вы видите расположение, оканчивающееся на
databinding
данных, это означает, что произошла ошибка привязки данных. - В XML-файле макета проверьте наличие ошибок в атрибутах
onClick
, использующих привязку данных. Найдите функцию, которую вызывает лямбда-выражение, и убедитесь, что она существует. - В разделе
<data>
XML проверьте правильность написания переменной привязки данных.
Например, обратите внимание на неправильное написание имени функции onCorrect()
в следующем значении атрибута:
android:onClick="@{() -> gameViewModel.onCorrectx()}"
Также обратите внимание на неправильное написание gameViewModel
в разделе <data>
XML-файла:
<data>
<variable
name="gameViewModelx"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
Android Studio не обнаруживает подобные ошибки, пока вы не скомпилируете приложение, а затем компилятор выводит сообщение об ошибке, подобное следующему:
error: cannot find symbol import com.example.android.guesstheword.databinding.GameFragmentBindingImpl" symbol: class GameFragmentBindingImpl location: package com.example.android.guesstheword.databinding
Привязка данных хорошо работает с LiveData
, которая используется с объектами ViewModel
. Теперь, когда вы добавили привязку данных к объектам ViewModel
, вы готовы включить LiveData
.
В этой задаче вы изменяете приложение GuessTheWord, чтобы использовать LiveData
в качестве источника привязки данных для уведомления пользовательского интерфейса об изменениях данных без использования методов наблюдателя LiveData
.
Шаг 1: Добавьте слово LiveData в файл game_fragment.xml.
На этом шаге вы привязываете текущее текстовое представление слова непосредственно к объекту LiveData
в ViewModel
.
- В
game_fragment.xml
добавьте атрибутandroid:text
в текстовое представлениеword_text
.
Установите его в объект LiveData
, word
из GameViewModel
, используя переменную привязки gameViewModel
.
<TextView
android:id="@+id/word_text"
...
android:text="@{gameViewModel.word}"
... />
Обратите внимание, что вам не нужно использовать word.value
. Вместо этого вы можете использовать реальный объект LiveData
. Объект LiveData
отображает текущее значение word
. Если значение word
равно null, объект LiveData
отображает пустую строку.
- В
GameFragment
вonCreateView()
после инициализацииgameViewModel
установите текущую активность в качестве владельца жизненного цикла переменнойbinding
. Это определяет область действия объектаLiveData
выше, позволяя объекту автоматически обновлять представления в макете,game_fragment.xml
.
binding.gameViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
- В
GameFragment
удалите наблюдателя дляword
LiveData
.
Код для удаления:
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
- Запустите свое приложение и играйте в игру. Теперь текущее слово обновляется без метода наблюдателя в UI-контроллере.
Шаг 2. Добавьте LiveData оценки в файл score_fragment.xml.
На этом шаге вы привязываете score
LiveData
к текстовому представлению оценки во фрагменте оценки.
- В
score_fragment.xml
добавьте атрибутandroid:text
в текстовое представление партитуры. НазначьтеscoreViewModel.score
text
атрибуту. Посколькуscore
является целым числом, преобразуйте ее в строку с помощьюString.valueOf()
.
<TextView
android:id="@+id/score_text"
...
android:text="@{String.valueOf(scoreViewModel.score)}"
... />
- В
ScoreFragment
после инициализацииscoreViewModel
установите текущую активность в качестве владельца жизненного цикла переменнойbinding
.
binding.scoreViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
- В
ScoreFragment
удалите наблюдателя для объектаscore
.
Код для удаления:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Запустите свое приложение и играйте в игру. Обратите внимание, что оценка во фрагменте оценки отображается правильно, без наблюдателя во фрагменте оценки.
Шаг 3. Добавьте форматирование строк с привязкой данных
В макете можно добавить форматирование строк вместе с привязкой данных. В этой задаче вы форматируете текущее слово, чтобы добавить его в кавычки. Вы также форматируете строку оценки, добавляя к ней префикс « Текущая оценка» , как показано на следующем рисунке.
- В
string.xml
добавьте следующие строки, которые вы будете использовать для форматированияword
иscore
текстовых представлений.%s
и%d
являются заполнителями для текущего слова и текущего счета.
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
- В
game_fragment.xml
обновите атрибутtext
текстового представленияword_text
, чтобы использовать ресурс строкиquote_format
. Передайте вgameViewModel.word
. Это передает текущее слово в качестве аргумента в строку форматирования.
<TextView
android:id="@+id/word_text"
...
android:text="@{@string/quote_format(gameViewModel.word)}"
... />
- Отформатируйте текстовое представление
score
аналогичноword_text
. Вgame_fragment.xml
добавьте атрибутtext
в текстовое представлениеscore_text
. Используйте строковый ресурсscore_format
, который принимает один числовой аргумент, представленный заполнителем%d
. Передайте объектLiveData
,score
, в качестве аргумента этой строки форматирования.
<TextView
android:id="@+id/score_text"
...
android:text="@{@string/score_format(gameViewModel.score)}"
... />
- В классе
GameFragment
внутриonCreateView()
удалите код наблюдателя заscore
.
Код для удаления:
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Очистите, перестройте и запустите приложение, а затем запустите игру. Обратите внимание, что текущее слово и счет отформатированы на игровом экране.
Поздравляем! Вы интегрировали LiveData
и ViewModel
с привязкой данных в свое приложение. Это позволяет представлениям в вашем макете напрямую взаимодействовать с ViewModel
без использования обработчиков кликов во фрагменте. Вы также использовали объекты LiveData
в качестве источника привязки данных для автоматического уведомления пользовательского интерфейса об изменениях данных без использования методов наблюдателя LiveData
.
Проект Android Studio: GuessTheWord
- Библиотека привязки данных без проблем работает с компонентами архитектуры Android, такими как
ViewModel
иLiveData
. - Макеты в вашем приложении могут связываться с данными в компонентах архитектуры, которые уже помогают вам управлять жизненным циклом контроллера пользовательского интерфейса и уведомлять об изменениях в данных.
Привязка данных ViewModel
- Вы можете связать
ViewModel
с макетом, используя привязку данных. - Объекты
ViewModel
содержат данные пользовательского интерфейса. Передавая объектыViewModel
в привязку данных, вы можете автоматизировать часть связи между представлениями и объектамиViewModel
.
Как связать ViewModel
с макетом:
- В файл макета добавьте переменную привязки данных типа
ViewModel
.
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
- В файле
GameFragment
передайтеGameViewModel
в привязку данных.
binding.gameViewModel = viewModel
Привязки прослушивателя
- Привязки прослушивателя — это выражения привязки в макете, которые запускаются при запуске событий щелчка, таких как
onClick()
. - Привязки прослушивателя записываются как лямбда-выражения.
- Используя привязки прослушивателя, вы заменяете прослушиватели кликов в контроллерах пользовательского интерфейса привязками прослушивателя в файле макета.
- Привязка данных создает прослушиватель и устанавливает его в представлении.
android:onClick="@{() -> gameViewModel.onSkip()}"
Добавление LiveData к привязке данных
- Объекты
LiveData
можно использовать в качестве источника привязки данных для автоматического уведомления пользовательского интерфейса об изменениях в данных. - Вы можете привязать представление непосредственно к объекту
LiveData
вViewModel
. КогдаLiveData
вViewModel
изменяется, представления в макете могут автоматически обновляться без использования методов наблюдателя в контроллерах пользовательского интерфейса.
android:text="@{gameViewModel.word}"
- Чтобы привязка данных
LiveData
работала, установите текущую активность (контроллер пользовательского интерфейса) в качестве владельца жизненного цикла переменнойbinding
в контроллере пользовательского интерфейса.
binding.lifecycleOwner = this
Форматирование строк с привязкой данных
- Используя привязку данных, вы можете отформатировать строковый ресурс с заполнителями, такими как
%s
для строк и%d
для целых чисел. - Чтобы обновить
text
атрибут представления, передайте объектLiveData
в качестве аргумента в строку форматирования.
android:text="@{@string/quote_format(gameViewModel.word)}"
Удасити курс:
Документация для разработчиков Android:
В этом разделе перечислены возможные домашние задания для студентов, которые работают с этой кодовой лабораторией в рамках курса, проводимого инструктором. Инструктор должен сделать следующее:
- При необходимости задайте домашнее задание.
- Объясните учащимся, как сдавать домашние задания.
- Оценивайте домашние задания.
Преподаватели могут использовать эти предложения так мало или так часто, как они хотят, и должны свободно давать любые другие домашние задания, которые они считают подходящими.
Если вы работаете с этой кодовой лабораторией самостоятельно, не стесняйтесь использовать эти домашние задания, чтобы проверить свои знания.
Ответьте на эти вопросы
Вопрос 1
Какое из следующих утверждений о привязках слушателей неверно ?
- Привязки прослушивателя — это выражения привязки, которые запускаются, когда происходит событие.
- Привязки прослушивателя работают со всеми версиями плагина Android Gradle.
- Привязки прослушивателя записываются как лямбда-выражения.
- Привязки прослушивателя аналогичны ссылкам на методы, но они позволяют запускать произвольные выражения привязки данных.
вопрос 2
Предположим, что ваше приложение включает в себя этот строковый ресурс:
<string name="generic_name">Hello %s</string>
Что из следующего является правильным синтаксисом для форматирования строки с использованием выражения привязки данных?
-
android:text= "@{@string/generic_name(user.name)}"
-
android:text= "@{string/generic_name(user.name)}"
-
android:text= "@{@generic_name(user.name)}"
-
android:text= "@{@string/generic_name,user.name}"
Вопрос 3
Когда оценивается и запускается выражение привязки слушателя?
- Когда данные, хранящиеся в
LiveData
, изменяются - Когда действие воссоздается путем изменения конфигурации
- Когда происходит событие, такое как
onClick()
- Когда активность уходит в фон
Начать следующий урок:
Ссылки на другие лаборатории кода в этом курсе см. на целевой странице лаборатории кода Android Kotlin Fundamentals .