Навигация позволяет прикреплять данные к операции навигации, определяя аргументы для пункта назначения. Например, целевой странице профиля пользователя может потребоваться аргумент идентификатора пользователя, чтобы определить, какой профиль нужно показать.
В целом мы настоятельно рекомендуем передавать между целевыми страницами только минимальный объем данных. Например, для получения объекта следует передавать ключ, а не сам объект, поскольку общий объем памяти для всех сохраненных состояний в Android ограничен. Если вам нужно передать большой объем данных, используйте ViewModel, как описано в обзоре ViewModel.
Определение аргументов пункта назначения
Чтобы передавать данные между пунктами назначения, сначала определите аргумент, добавив его в пункт назначения, который его получает. Для этого выполните следующие действия:
- В редакторе навигации нажмите на целевой экран, который получает аргумент.
- На панели Атрибуты нажмите Добавить (+).
- В открывшемся окне Добавить ссылку на аргумент введите название аргумента, его тип, укажите, может ли он иметь значение null, и при необходимости задайте значение по умолчанию.
- Нажмите Добавить. Обратите внимание, что аргумент теперь отображается в списке Аргументы на панели Атрибуты.
- Затем нажмите на соответствующее действие, которое приведет вас к этому пункту назначения. На панели Атрибуты в разделе Значения аргументов по умолчанию вы увидите добавленный аргумент.
Вы также можете увидеть, что аргумент был добавлен в XML. Перейдите на вкладку Text (Текст), чтобы переключиться в режим XML, и обратите внимание, что аргумент был добавлен в пункт назначения, который его получает. Пример:
<fragment android:id="@+id/myFragment" > <argument android:name="myArg" app:argType="integer" android:defaultValue="0" /> </fragment>
Поддерживаемые типы аргументов
Библиотека Navigation поддерживает следующие типы аргументов:
| Тип | Синтаксис app:argType | Поддержка значений по умолчанию | Обрабатывается маршрутами | Допускает значение NULL |
|---|---|---|---|---|
| Целое число | app:argType="integer" | Да | Да | Нет |
| Плавающий режим | app:argType="float" | Да | Да | Нет |
| Долгота | app:argType="long" | Да. Значения по умолчанию всегда должны заканчиваться суффиксом L (например, 123L). | Да | Нет |
| Логическое значение | app:argType="boolean" | Да – "true" или "false" | Да | Нет |
| Строка | app:argType="string" | Да | Да | Да |
| Ссылка на ресурс | app:argType="reference" | Да.Значения по умолчанию должны быть указаны в формате "@resourceType/resourceName" (например, "@style/myCustomStyle") или "0". | Да | Нет |
| Пользовательский Parcelable | app:argType="<type>", где <type> – полное имя класса Parcelable. |
Значение по умолчанию – @null. Другие значения по умолчанию не поддерживаются. | Нет | Да |
| Пользовательский сериализуемый | app:argType="<type>", где <type> – полное имя класса Serializable. |
Поддерживает значение по умолчанию "@null". Другие значения по умолчанию не поддерживаются. | Нет | Да |
| Специальное перечисление | app:argType="<type>", где <type> – полное имя перечисления. | Да. Значения по умолчанию должны соответствовать неквалифицированному имени (например, "SUCCESS" для MyEnum.SUCCESS). | Нет | Нет |
Если тип аргумента поддерживает значения null, вы можете объявить значение по умолчанию null, используя android:defaultValue="@null".
Маршруты, ссылки на контент и URI с аргументами можно анализировать из строк. Это невозможно сделать с помощью пользовательских типов данных, таких как Parcelable и Serializable, как показано в таблице выше. Чтобы передавать сложные пользовательские данные, храните их в другом месте, например в ViewModel или базе данных, и передавайте только идентификатор во время навигации. Затем извлеките данные в новом месте после завершения навигации.
Если вы выберете один из специальных типов, откроется диалоговое окно Select Class (Выбор класса), в котором вам будет предложено выбрать соответствующий класс для этого типа. На вкладке Проект можно выбрать класс из текущего проекта.
Вы можете выбрать <inferred type>, чтобы библиотека Navigation определяла тип на основе предоставленного значения.
Вы можете установить флажок Массив, чтобы указать, что аргумент должен быть массивом выбранного значения Тип. объявлений.
- Массивы перечислений и массивы ссылок на ресурсы не поддерживаются.
- Массивы поддерживают значения NULL независимо от того, поддерживаются ли они базовым типом. Например, с помощью
app:argType="integer[]"можно использоватьapp:nullable="true", чтобы указать, что передача нулевого массива допустима. - Массивы поддерживают одно значение по умолчанию – "@null". Массивы не поддерживают другие значения по умолчанию.
Как переопределить аргумент целевой страницы в действии
Аргументы и значения по умолчанию на уровне пункта назначения используются всеми действиями, которые переходят к пункту назначения. При необходимости вы можете переопределить значение аргумента по умолчанию (или задать его, если оно ещё не существует), определив аргумент на уровне действия. Этот аргумент должен иметь то же имя и тип, что и аргумент, объявленный в пункте назначения.
В следующем XML-коде объявляется действие с аргументом, который переопределяет аргумент на уровне целевой страницы из предыдущего примера:
<action android:id="@+id/startMyFragment"
app:destination="@+id/myFragment">
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="1" />
</action>
Используйте Safe Args для передачи данных с типобезопасностью
В компоненте Navigation есть плагин Gradle под названием Safe Args, который генерирует простые классы объектов и компоновщиков для типобезопасной навигации и доступа к любым связанным аргументам. Для навигации и передачи данных настоятельно рекомендуется использовать Safe Args, поскольку этот плагин обеспечивает типобезопасность.
Если вы не используете Gradle, то не сможете использовать плагин Safe Args. В таких случаях для передачи данных можно использовать пакеты.
Чтобы добавить функцию Safe Args в ваш проект, укажите следующий classpath в файле build.gradle верхнего уровня:
Классный
buildscript { repositories { google() } dependencies { def nav_version = "2.10.2" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } }
Котлин
buildscript { repositories { google() } dependencies { val nav_version = "2.10.2" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") } }
Вам также необходимо установить один из двух доступных плагинов.
Чтобы сгенерировать код на языке Java, подходящий для модулей Java или смешанных модулей Java и Kotlin, добавьте следующую строку в файл build.gradle вашего приложения или модуля :
Классный
plugins { id 'androidx.navigation.safeargs' }
Котлин
plugins { id("androidx.navigation.safeargs") }
В качестве альтернативы, для генерации кода Kotlin, подходящего для модулей, работающих только с Kotlin, добавьте:
Классный
plugins { id 'androidx.navigation.safeargs.kotlin' }
Котлин
plugins { id("androidx.navigation.safeargs.kotlin") }
В файле gradle.properties необходимо добавить android.useAndroidX=true как описано в разделе "Переход на AndroidX" .
После включения Safe Args сгенерированный код будет содержать следующие типобезопасные классы и методы для каждого действия, а также для каждого пункта отправки и получения.
Для каждого целевого ресурса, в котором инициируется действие, создается класс. Название этого класса состоит из названия пункта назначения и слова Directions. Например, если исходным пунктом назначения является фрагмент с именем
SpecifyAmountFragment, сгенерированный класс будет называтьсяSpecifyAmountFragmentDirections.В этом классе есть метод для каждого действия, определенного в исходном пункте назначения.
Для каждого действия, используемого для передачи аргумента, создается внутренний класс, имя которого основано на действии. Например, если действие называется
confirmationAction,, то класс будет называтьсяConfirmationAction. Если в действии есть аргументы безdefaultValue, то для установки их значений используется связанный класс действия.Для целевого местоположения создается класс. Название этого класса – название пункта назначения с добавлением слова Args. Например, если целевой фрагмент называется
ConfirmationFragment,, то сгенерированный класс будет называтьсяConfirmationFragmentArgs. Чтобы получить аргументы, используйте методfromBundle()этого класса.
В примере ниже показано, как использовать эти методы, чтобы задать аргумент и передать его методу navigate().
Kotlin
override fun onClick(v: View) { val amountTv: EditText = view!!.findViewById(R.id.editTextAmount) val amount = amountTv.text.toString().toInt() val action = SpecifyAmountFragmentDirections.confirmationAction(amount) v.findNavController().navigate(action) }
Java
@Override public void onClick(View view) { EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount); int amount = Integer.parseInt(amountTv.getText().toString()); ConfirmationAction action = SpecifyAmountFragmentDirections.confirmationAction(); action.setAmount(amount); Navigation.findNavController(view).navigate(action); }
В коде целевого приложения используйте метод getArguments(), чтобы получить пакет и использовать его содержимое. При использовании зависимостей -ktx пользователи Kotlin также могут использовать делегат свойства by navArgs() для доступа к аргументам.
Kotlin
val args: ConfirmationFragmentArgs by navArgs() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tv: TextView = view.findViewById(R.id.textViewAmount) val amount = args.amount tv.text = amount.toString() }
Java
@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { TextView tv = view.findViewById(R.id.textViewAmount); int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount(); tv.setText(amount + ""); }
Как использовать Safe Args с глобальным действием
При использовании Safe Args с глобальным действием необходимо указать значение android:id для корневого элемента <navigation>, как показано в примере ниже.
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_nav" app:startDestination="@id/mainFragment"> ... </navigation>
Навигация генерирует класс Directions для элемента <navigation> на основе значения android:id. Например, если у вас есть элемент <navigation> с android:id=@+id/main_nav, сгенерированный класс будет называться MainNavDirections. Для всех пунктов назначения в элементе <navigation> сгенерированы методы доступа ко всем связанным глобальным действиям с использованием тех же методов, что описаны в предыдущем разделе.
Как передавать данные между пунктами назначения с помощью объектов Bundle
Если вы не используете Gradle, вы все равно можете передавать аргументы между пунктами назначения, используя объекты Bundle. Создайте объект Bundle и передайте его в целевой сервис с помощью navigate(), как показано в примере ниже.
Kotlin
val bundle = bundleOf("amount" to amount) view.findNavController().navigate(R.id.confirmationAction, bundle)
Java
Bundle bundle = new Bundle(); bundle.putString("amount", amount); Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
В коде целевой страницы используйте метод getArguments(), чтобы получить Bundle и использовать его содержимое:
Kotlin
val tv = view.findViewById<TextView>(R.id.textViewAmount) tv.text = arguments?.getString("amount")
Java
TextView tv = view.findViewById(R.id.textViewAmount); tv.setText(getArguments().getString("amount"));
Передача данных в начальный пункт назначения
Вы можете передавать данные в начальный пункт назначения приложения. Сначала нужно создать объект Bundle, который будет содержать данные. Затем используйте один из следующих подходов, чтобы передать Bundle в начальный пункт назначения:
- Если вы создаете
NavHostпрограммно, вызовитеNavHostFragment.create(R.navigation.graph, args), гдеargs– этоBundle, содержащий ваши данные. - В противном случае вы можете задать аргументы начального пункта назначения, вызвав одну из следующих перегрузок
NavController.setGraph():- Используйте идентификатор графа:
navController.setGraph(R.navigation.graph, args). - Используйте сам график:
navController.setGraph(navGraph, args).
- Используйте идентификатор графа:
Чтобы получить данные о пункте отправления, вызовите Fragment.getArguments().
Особенности работы с ProGuard
Если вы сокращаете код, необходимо предотвратить обфускацию названий классов Parcelable, Serializable и Enum в процессе минификации. Это можно сделать двумя способами:
- Используйте аннотации @Keep.
- Используйте правила keepnames.
Ниже описаны эти подходы.
Как использовать аннотации @Keep
В следующем примере в определения классов моделей добавляются аннотации @Keep:
Kotlin
@Keep class ParcelableArg : Parcelable { ... } @Keep class SerializableArg : Serializable { ... } @Keep enum class EnumArg { ... }
Java
@Keep public class ParcelableArg implements Parcelable { ... } @Keep public class SerializableArg implements Serializable { ... } @Keep public enum EnumArg { ... }
Как использовать правила keepnames
Вы также можете добавить правила keepnames в файл proguard-rules.pro, как показано в примере ниже.
proguard-rules.pro
...
-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg
...
Дополнительные ресурсы
Чтобы узнать больше о навигации, ознакомьтесь со следующими дополнительными ресурсами.