Архитектура навигации Component- Передача данных аргумента в startDestination

У меня есть активность A, которая запускает активность B, передавая ей некоторые данные о намерениях. В Activity B есть навигационный граф из нового компонента Navigation Architecture. Я хочу передать эти данные намерения в фрагмент startDestination в качестве аргумента, как это сделать?

Ответ 1

Хорошо, я нашел решение этой проблемы благодаря Яну Лейку из команды Google. Допустим, у вас есть действие A, которое запустит действие B с некоторыми намеренными данными, и вы хотите получить эти данные в startDestination. У вас есть два варианта здесь, если вы используете безопасные аргументы, что в моем случае можно сделать

StartFragmentArgs.fromBundle(requireActivity().intent?.extras)

читать аргументы из намерения. Если вы не используете безопасные аргументы, вы можете извлечь данные из пакета, который вы используете самостоятельно с помощью requireActivity().intent?.extras который вернет Bundle, который вы можете использовать вместо метода getArguments() фрагмента. Вот это я пробую и все отлично работает.

Ответ 2

TL;DR: вам нужно вручную раздуть график, добавить ключи/значения в значение по умолчанию и создать график на navController.

Шаг 1

В документации указано, что вы установите график в <fragment> в макете Activity. Что-то вроде:

<fragment
    android:id="@+id/navFragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    app:graph="@navigation/nav_whatever"
    app:defaultNavHost="true"
    />

УДАЛИТЬ линию, устанавливающую graph=.

Шаг 2

В Activity, который будет отображать ваш NavHostFragment, NavHostFragment граф следующим образом:

val navHostFragment = navFragment as NavHostFragment
val inflater = navHostFragment.navController.navInflater
val graph = inflater.inflate(R.navigation.nav_whatever)

Где navFragment - это идентификатор, который вы предоставили вашему фрагменту в XML, как указано выше.

Шаг 3 [Важный!]

Создайте пакет для хранения аргументов, которые вы хотите передать в ваш фрагмент startDestination и добавьте его в аргументы по умолчанию графика:

val bundle = Bundle()
// ...add keys and values
graph.addDefaultArguments(bundle)

Шаг 4

Установите график на хосте navController:

navHostFragment.navController.graph = graph

Ответ 3

Это было исправлено в 1.0.0-alpha07. См детали.

Решение похоже на ответ Эллиота Шрока, но с использованием официального API.

Надо вручную раздувать NavHostFragment или graph

использование

NavHostFragment.create(R.navigation.graph, args)

Или же

navController.setGraph(R.navigation.graph, args)

Аргументы - это данные, которые мы хотим передать в пункт назначения.

Ответ 4

Я думаю, что это изменилось снова с выпуском 1.0.0. И Google очень хорошо спрятал эту информацию в официальной документации. Или, по крайней мере, я изо всех сил пытался найти его, но наткнулся на него в руководстве Переход к компоненту навигации. Как передать аргументы в начальный пункт назначения, описано здесь:

https://developer.android.com/guide/navigation/navigation-migrate#pass_activity_destination_args_to_a_start_destination_fragment

Короче говоря

  1. Вы должны установить график навигации программно:
findNavController(R.id.main_content)
                .setGraph(R.navigation.product_detail_graph, intent.extras)
  1. Не устанавливайте график в объявлении NavHostFragment XML.
  2. Прочитайте дополнения со стороны получателя:
val args by navArgs<ProductDetailsArgs>()  
val productId = args.productId

Обновление: Google заявил, что официальная документация для передачи аргументов исходной цели навигации действительно отсутствует. Надеемся, что это будет добавлено в ближайшее время как часть документации по компоненту навигации.

Ответ 5

Так что для людей все еще борющихся с этим. Я нашел другой способ сделать это, не используя Safe-Args и шаг, используя @Elliot Answer.

Допустим, вы получили некоторые аргументы в Деятельности B от Деятельности A, и в вашей Деятельности B есть фрагмент startDestination, который вы инициализируете контроллер Nav следующим образом:

navController = Navigation.findNavController(this, R.id.detailFragment);

из Nav Controller у вас будет доступ к вашему графику, который вы установили в XML следующим образом, и вы можете установить аргументы в defaultArguments:

navController.getGraph().addDefaultArguments(extras);

Примечание: это также обновит значения ключей, если они уже присутствуют в графе xml

Теперь в вашем фрагменте вы должны найти аргументы по умолчанию из вашего NavHostFragment, например:

Bundle defaultArguments = NavHostFragment.findNavController(this).getGraph().getDefaultArguments();

и у вас будут ценности там. Я не знаю, почему @Elliot считает это важным, но так и должно быть?

ОБНОВЛЕНИЕ альфа09: аргумент addDefault больше не поддерживается в этой версии, вы должны использовать NavArgument

Ответ 6

На момент написания этого ответа я использовал Navigation 2.2.0-alpha01

Если вы хотите передать некоторые данные в место назначения непосредственно в качестве аргументов из активности хоста, вам нужно вручную установить график навигации хостов в методе активности хоста onCreate(), как показано ниже:

Получите ваш navController:

val navController by lazy { findNavController(R.id.<your_nav_host_id>) }

Тогда в действии хозяина onCreate()

val bundle = Bundle()
bundle.putString("some_argument", "some_value")
navController.setGraph(R.navigation.<you_nav_graph_xml>, bundle)

Или, если вы хотите передать все намерения дополнительно к startDestination:

navController.setGraph(R.navigation.<you_nav_graph_xml>, intent.extras)

Поскольку intent.extras вернет только Bundle

Когда вы устанавливаете navGraph с помощью метода setGraph(), вам следует избегать установки атрибута app: NavGraph в определение NavHostFragment, потому что это приводит к завышению и настройте график навигации дважды.

При чтении этих аргументов в вашем фрагменте startDestination:

Если вы используете Safe Args Plugin (что очень рекомендуется), то в вашем фрагменте:

private val args by navArgs<DummyFragmentArgs>()

Плагин Safe Args генерирует класс Args, добавляя Args к имени вашего фрагмента. Например, если ваш фрагмент называется DummyFragment, тогда Safe Args создаст класс с именем DummyFragmentArgs

где navArgs<> - это функция расширения, определенная в Android KTX

Если вы не используете Android KTX, вы можете получить объект args, например:

val args = DummyFragmentArgs.fromBundle(arguments!!)

После того как вы приобрели объект arguments, вы можете просто получить свои аргументы:

args.someArgument

Обратите внимание, как мы передали "some_argument" в качестве аргумента, и мы читаем его как someArgument, используя Safe Args

Если вы не используете Safe Args (хотя нет причин не использовать его), вы можете получить доступ к своим аргументам следующим образом:

arguments?.getString("some_argument")

Все это описано в документации Migrate to Navigation Component здесь:https://developer.android.com/guide/navigation/navigation-migrate#pass_activity_destination_args_to_a_start_destination_fragment

Ответ 7

addDefaultArguments больше не присутствует в последних версиях библиотеки. Я исправил проблему следующим образом:

        val navHostFragment = fragment_navigation_onboarding as NavHostFragment
        val navController = navHostFragment.navController
        val navInflater = navController.navInflater
        val graph:NavGraph = navInflater.inflate(R.navigation.navigation_your_xml)
        val model = Model()//you might get it from another activity
        graph.addArgument("Data", NavArgument.Builder().setDefaultValue(model).build()) // This is where you pass the bundle data from Activity to StartDestination
        navHostFragment.navController.graph = graph

Ответ 8

Я нашел решение после некоторых исследований. Он работает с последней версией библиотеки навигации. Обратитесь к приведенному ниже коду:

  1. Добавьте это в свой макет деятельности. Примечание. Мы не устанавливаем аргумент app: navGraph в файле xml. Мы установим его динамически.

    <fragment
        android:id="@+id/fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        app:defaultNavHost="true" />
    
  2. В своем Java файле активности напишите приведенный ниже код и внесите соответствующие изменения. Используйте NavArgument, чтобы передать значение аргумента и добавить аргумент в пользовательский Navgraph, а затем установить график.

    public class YourActivity extends AppCompatActivity {
         private NavArgument nameArg, mailArg;
    
         @Override
         protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.profile);
            nameArg = new NavArgument.Builder().setDefaultValue("your name").build();
            mailArg = new NavArgument.Builder().setDefaultValue("your email id").build();
            NavController navController = Navigation.findNavController(this, R.id.fragment);
            NavInflater navInflater = navController.getNavInflater();
            NavGraph navGraph = navInflater.inflate(R.navigation.nav_profile_graph);
            navGraph.addArgument("your name key", nameArg);
            navGraph.addArgument("your mail key", mailArg);
            navController.setGraph(navGraph);
        }
    }
    
  3. Напишите навигационную диаграмму ниже и добавьте те же ключи аргументов в начальный фрагмент.

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto"
       xmlns:tools="http://schemas.android.com/tools"
       android:id="@+id/nav_graph"
       app:startDestination="@+id/profile_basic">
    
       <fragment
          android:id="@+id/profile_basic"
          android:name="com.yourpackage.ProfileBasicFragment"
          android:label="Profile Basic"
          tools:layout="@layout/fragment_profile_basic">
    
          <argument android:name="your name key"
              app:argType="string"/>
    
          <argument android:name="your mail key"
              app:argType="string"/>
    
       </fragment>
    </navigation>
    
  4. В вашем фрагменте просто извлеките значения с помощью функции getArguments().

    String name = getArguments().getString("your name key");
    String mail = getArguments().getString("your mail key");