Передача данных между пунктами назначения, Передача данных между пунктами назначения

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

В общем, вам следует предпочесть передачу только минимального объема данных между пунктами назначения. Например, вам следует передать ключ для получения объекта, а не передавать сам объект, поскольку общее пространство для всех сохраненных состояний в Android ограничено. Если вам нужно передать большие объемы данных, используйте ViewModel , как описано в обзоре ViewModel .

Определите аргументы назначения

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

  1. В редакторе навигации щелкните пункт назначения, который получает аргумент.
  2. На панели «Атрибуты» нажмите «Добавить» ( + ).
  3. В появившемся окне «Добавить ссылку на аргумент» введите имя аргумента, тип аргумента, значение, допускающее значение NULL, и значение по умолчанию, если необходимо.
  4. Нажмите Добавить . Обратите внимание, что аргумент теперь отображается в списке «Аргументы» на панели «Атрибуты» .
  5. Затем нажмите соответствующее действие, которое приведет вас к этому месту назначения. На панели «Атрибуты» вы теперь должны увидеть новый добавленный аргумент в разделе «Значения аргумента по умолчанию» .
  6. Вы также можете видеть, что аргумент был добавлен в XML. Перейдите на вкладку «Текст» , чтобы переключиться на представление XML, и обратите внимание, что ваш аргумент был добавлен в пункт назначения, который получает аргумент. Пример показан ниже:

     <fragment android:id="@+id/myFragment" >
         <argument
             android:name="myArg"
             app:argType="integer"
             android:defaultValue="0" />
     </fragment>
    

Поддерживаемые типы аргументов

Библиотека навигации поддерживает следующие типы аргументов:

Тип синтаксис приложения:argType Поддержка значений по умолчанию Обрабатывается по маршрутам Обнуляемый
Целое число приложение:argType="целое число" Да Да Нет
Плавать приложение:argType="плавающее" Да Да Нет
Длинный приложение:argType="длинный" Да. Значения по умолчанию всегда должны заканчиваться суффиксом «L» (например, «123L»). Да Нет
логическое значение приложение:argType="логическое" Да – «правда» или «ложь». Да Нет
Нить приложение:argType="строка" Да Да Да
Справочник ресурсов приложение:argType="ссылка" Да. Значения по умолчанию должны быть в форме «@resourceType/resourceName» (например, «@style/myCustomStyle») или «0». Да Нет
Пользовательские посылки 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 с их аргументами можно анализировать из строк. Это невозможно при использовании пользовательских типов данных, таких как Parcelables и Serializables, как показано в предыдущей таблице. Чтобы передавать пользовательские сложные данные, храните их в другом месте, например в ViewModel или базе данных, и передавайте идентификатор только во время навигации; затем извлеките данные в новом месте после завершения навигации.

Когда вы выбираете один из пользовательских типов, появляется диалоговое окно «Выбор класса» , в котором вам предлагается выбрать соответствующий класс для этого типа. Вкладка «Проект» позволяет выбрать класс из текущего проекта.

Вы можете выбрать <inferred type>, чтобы библиотека навигации определяла тип на основе предоставленного значения.

Вы можете установить флажок «Массив» , чтобы указать, что аргумент должен быть массивом выбранного значения типа . Обратите внимание на следующее:

  • Массивы перечислений и массивы ссылок на ресурсы не поддерживаются.
  • Массивы поддерживают значения, допускающие значение NULL, независимо от поддержки значений, допускающих значение 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 для передачи данных с безопасностью типов

Компонент навигации имеет плагин Gradle под названием Safe Args, который генерирует простые классы объектов и конструкторов для типобезопасной навигации и доступа к любым связанным аргументам. Safe Args настоятельно рекомендуется использовать для навигации и передачи данных, поскольку он обеспечивает безопасность типов.

Если вы не используете Gradle, вы не сможете использовать плагин Safe Args. В этих случаях вы можете использовать пакеты для прямой передачи данных.

Чтобы добавить Safe Args в свой проект, включите следующий classpath в файл build.gradle верхнего уровня:

классный

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.8.1"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Котлин

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.8.1"
        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")
}

У вас должно быть android.useAndroidX=true в файле gradle.properties в соответствии с переходом на AndroidX .

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

  • Класс создается для каждого пункта назначения, из которого происходит действие. Имя этого класса представляет собой имя исходного пункта назначения, к которому добавляется слово «Направления». Например, если исходным местом назначения является фрагмент с именем SpecifyAmountFragment , созданный класс называется SpecifyAmountFragmentDirections .

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

  • Для каждого действия, используемого для передачи аргумента, создается внутренний класс, имя которого основано на этом действии. Например, если действие называется confirmationAction, класс будет называться ConfirmationAction . Если ваше действие содержит аргументы без defaultValue , вы используете связанный класс действия, чтобы установить значение аргументов.

  • Для получателя создается класс. Имя этого класса представляет собой имя пункта назначения, к которому добавлено слово «Args». Например, если целевой фрагмент называется ConfirmationFragment, созданный класс называется ConfirmationFragmentArgs . Используйте метод fromBundle() этого класса для получения аргументов.

В следующем примере показано, как использовать эти методы для установки аргумента и передачи его методу navigate() :

Котлин

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)
}

Ява

@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() для доступа к аргументам.

Котлин

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()
}

Ява

@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() , как в следующем примере:

Котлин

val bundle = bundleOf("amount" to amount)
view.findNavController().navigate(R.id.confirmationAction, bundle)

Ява

Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);

В коде получателя используйте метод getArguments() для получения Bundle и использования его содержимого:

Котлин

val tv = view.findViewById<TextView>(R.id.textViewAmount)
tv.text = arguments?.getString("amount")

Ява

TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));

Передача данных в начальный пункт назначения

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

Чтобы получить данные в начальном пункте назначения, вызовите Fragment.getArguments() .

Рекомендации по ProGuard

Если вы сокращаете свой код , вам необходимо предотвратить запутывание имен классов Parcelable , Serializable и Enum в рамках процесса минимизации. Вы можете сделать это одним из двух способов:

  • Используйте аннотации @Keep.
  • Используйте правила сохранения имен.

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

Используйте аннотации @Keep

В следующем примере аннотации @Keep добавляются к определениям классов модели:

Котлин

@Keep class ParcelableArg : Parcelable { ... }

@Keep class SerializableArg : Serializable { ... }

@Keep enum class EnumArg { ... }

Ява

@Keep public class ParcelableArg implements Parcelable { ... }

@Keep public class SerializableArg implements Serializable { ... }

@Keep public enum EnumArg { ... }

Используйте правила сохранения имен

Вы также можете добавить правила 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

...

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

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

Образцы

Кодлабы

Видео