Navigation. Передання даних. Type-safe аргументи.

Щоб під час виклику destination передати дані, необхідно використовувати Bundle. Для нього залишили містечко в методі navigate.

Bundle bundle = new Bundle();
bundle.putString("arg1", "value1");
bundle.putInt("arg2", 2);
navController.navigate(R.id.fragment2, bundle);

Створюємо Bundle, наповнюємо аргументами і передаємо в метод navigate разом з ID destination (або action).

У фрагменті fragment2 метод getArguments поверне такий Bundle:

Bundle[{arg1=value1, arg2=2}]

Відповідно отримати дані можна стандартним шляхом:

String arg1Value = getArguments().getString("arg1");
Integer arg2Value = getArguments().getInt("arg2");

А якщо викликали не фрагмент, а Activity, то так:

String arg1Value = getIntent().getStringExtra("arg1");
Integer arg2Value = getIntent().getIntExtra("arg2", 0);

Аргументи в графі

У графі ми можемо для destination додати аргументи і прописати їм значення за замовчуванням.

Секція Arguments. Вказуємо ім'я аргумента, тип і значення за замовчуванням. Тип може бути string, integer і reference.

Якщо тип reference, то ми можемо вказати ідентифікатор будь-якого ресурсу. У цьому прикладі я вказав dimen і string ресурси. Вони мають такі значення.

<dimen name="some_size">20dp</dimen>
<string name="app_name">Navigation</string>

Давайте перевіримо, як працюють ці значення за замовчуванням. Знову викличемо fragment2 і при цьому не задаватимемо жодних значень для аргументів, які щойно створили.

Bundle bundle = new Bundle();
bundle.putString("arg1", "value1");
bundle.putInt("arg2", 2);
navController.navigate(R.id.fragment2, bundle);

У результаті getArguments у фрагменті має такий вигляд:

Bundle[{arg1=value1, arg2=2, arg3=defaultValue3, arg4=52, arg5=Navigation}]

Для аргументів arg3, arg4 і arg5 прийшли дефолтні значення. Причому reference ідентифікатори були конвертовані у відповідні їм значення. arg4 треба отримувати, як Int, а arg5 - як String.

Значення за замовчуванням будуть приходити, якщо ми в коді не помістили в Bundle якесь своє значення аргументу.

Якщо для аргументу немає значення за замовчуванням у графі і в коді нічого не було задано в Bundle, то в destination цей аргумент просто не прийде.

Type-safe

Студія може згенерувати нам класи і методи для зручного передавання аргументів. Для цього нам знадобиться safeargs плагін.

У build.gradle файл проєкту в секцію buildscript > dependencies необхідно додати classpath

buildscript {
    repositories {
        google()
    }
    dependencies {
        classpath "android.arch.navigation:navigation-safe-args-ini-plugin:1.0.0-alpha01"
    }
}

А в build.gradle модуля додаємо плагін safeargs

apply plugin: 'com.android.application'
apply plugin: 'androidx.navigation.safeargs'
 
android {
   //...
}

Для повноцінного використання плагіна необхідно використовувати action.

Додамо action, який вестиме з fragment1 до fragment2 і вкажемо ID = actionToFragment2.

Зверніть увагу, що action підтягнув аргументи з destination, в який він веде, тобто з fragment2. І ми можемо вказати для них значення за замовчуванням.

Отже, у нас у графі є три компоненти, які беруть участь у навігації:

  • destination, якому належить (з якого виходить) action (ID = fragment1)
  • action (ID = actionToFrgament2)
  • destination, у який веде action (ID = fragment2)

Для кожного з них буде згенеровано свій клас. Якщо класи не генеруються, натисніть Ctrl+F9.

Відправник

Для fragment1 буде згенеровано клас Fragment1Directions. Тобто як ім'я взято ID і додано слово Directions. У цьому класі буде метод actionToFragment2(), який дасть нам action ActionToFragment2.

action

ActionToFragment2 - згенерований клас для action actionToFragment2. У цього класу є методи, що відповідають аргументам цього action (див. скріншот вище). Тобто для аргументів arg3, arg4 і arg5 будуть створені методи:

setArg3(String arg3)
setArg4(int arg4)
setArg5(int arg5)

і ми зможемо їх використовувати, щоб задавати значення аргументів. Під капотом там все також використовується Bundle.

Код навігації матиме такий вигляд:

Fragment1Directions.ActionToFragment2 action = Fragment1Directions.actionToFragment2();
action.setArg3("value3").setArg4(R.dimen.some_other_size).setArg5(R.string.hello_blank_fragment);
navController.navigate(action);

Отримуємо ActionToFragment2 з Fragment1Directions, задаємо значення аргументів і передаємо цей action у метод navigate.

Одержувач

Для одержувача буде згенеровано клас Fragment2Args. Тобто для імені використовується ID destination + суфікс Args.

У цього класу будуть створені методи для вилучення даних з bundle.

Статичний метод, для створення Fragment2Args з передачею йому bundle.

public static Fragment2Args fromBundle(Bundle bundle)

І методи отримання значення аргументів.

public String getArg3()
public int getArg4()
public int getArg5()

Код вилучення аргументів матиме такий вигляд:

Fragment2Args fragment2Args = Fragment2Args.fromBundle(getArguments())
String arg3Value = fragment2Args.getArg3();
Integer arg4Value = fragment2Args.getArg4();
Integer arg5Value = fragment2Args.getArg5();

А якщо одержувач - Activity, то код такий:

String arg3Value = SecondActivityArgs.fromBundle(getIntent().getExtras()).getArg3();

Ці згенеровані класи дуже прості, ви завжди можете відкрити їх і подивитися код. Загалом вони є обгорткою над Bundle.