Компонент архитектуры навигации Android: как передать данные пакета в startDestination

У меня есть деятельность, которая имеет NavHostFragment, Деятельность получает определенные значения в своем намерении. Я хочу передать эти данные в первый фрагмент, т.е. startDestination навигационного графика. Я не смог найти никакой документации по этому поводу.

Я уже прошел этот вопрос на SO, но я не могу найти addDefaultArguments метод для navController.getGraph(),

Можно ли передать пакет в startDestination?

9 ответов

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

На момент написания этого ответа я использую 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:

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

private val args by navArgs<DummyFragmentArgs>()

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

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

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

val args = DummyFragmentArgs.fromBundle(arguments!!)

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

args.someArgument

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

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

arguments?.getString("some_argument")

Все это задокументировано в документации по переходу на компонент навигации здесь:https://developer.android.com/guide/navigation/navigation-migrate

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

  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");
    

Я тоже сталкивался с той же проблемой,

Вот как я решил это:

  1. Удалите настройку xml NavHostFragment из your_activity.xml: т.е. удалить приложение:navGraph="@navigation/nav_graph

Вот как должен выглядеть ваш XML.

        <fragment
        android:id="@+id/nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        />
  1. Добавить настройки для NavHostFragment программно в onCreate() деятельности. И передать данные пакета, используя NavGraph.addDefaultArguments(bundleData) апи

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.editor_layout)
            setupNavigation()
        }
    
        private fun setupNavigation() {
            val navHostFragment = nav_host as NavHostFragment
            val navController = navHostFragment.navController
            val navInflater = navController.navInflater
            val graph = navInflater.inflate(R.navigation.nav_graph)
            graph.addDefaultArguments(intent!!.extras!!) // This is where you pass the bundle data from Activity to StartDestination 
            navHostFragment.navController.graph = graph
        }
    

ОБНОВИТЬ:

Зависимости в моем файле Project Gradle:

dependencies {

    def nav_version = "1.0.0-alpha08"

    implementation "android.arch.navigation:navigation-fragment:$nav_version" // use -ktx for Kotlin
    implementation "android.arch.navigation:navigation-ui:$nav_version" // use -ktx for Kotlin}
}

ПРИМЕЧАНИЕ: в версии компонента навигации 1.0.0-alpha09 по какой-то причине у Google нет метода, как addDefaultArguments() может быть исправлено в ближайшее время. Но более низкие версии имеют addDefaultArguments() Я проверил как в Java, так и в Kotlin, поэтому попробуйте использовать 1.0.0-alpha07 или же 1.0.0-alpha08

Я проверил исходный код и увидел, что есть много изменений в отношении назначения навигации и аргументов. Я думаю, что правильный способ передачи аргументов в начало назначения - это использование метода addArgument, что-то вроде этого:

val argument1 = 1 //First value
val argument2 = "Value" //Second value
val navArgument1=NavArgument.Builder().setDefaultValue(argument1).build()
val navArgument2=NavArgument.Builder().setDefaultValue(argument2).build()
navController.getGraph().addArgument("Key1",navArgument1)
navController.getGraph().addArgument("Key2",navArgument2)

Может быть, есть лучший способ, но я его не нашел.

Вы можете добавить аргументы в свой график, как это

<fragment
  android:id="@+id/exampleFragment"
  android:label="example_fragment"
  android:name="com.example.yourapp.ui.ExampleFragment"
  tools:layout="@layout/example_fragment">
<argument
  android:name="exampleArgs"
  app:argType="reference"
  android:defaultValue="@string/example"/>
</fragment>

https://developer.android.com/guide/navigation/navigation-pass-data

Хотя это поздний ответ, существует очень простой способ передачи пакетов с использованием навигационного контроллера/архитектуры навигации:

Фрагмент, который будет отправлять данные

      //Best to put in onViewCreated
val navController = findNavController()

//Put in code where you want to start navigation
val b = Bundle()
b.putString("key_1", "Data 1")
b.putString("key_2", "Data 2")

navController.navigate(R.id.action_signupFragment_to_signinFragment,b)

Фрагмент, который будет получать данные

      val data_1: String = arguments?.getString("key_1")?:""
val data_1: String = arguments?.getString("key_2")?:"" 

В зависимости от типа данных, которые вы отправляете, вам необходимо обновить безопасный вызов или сделать переменные обнуляемыми.

Навигация 1.0.0

        val navHostFragment = root_nav_host_fragment as NavHostFragment
        val navController = navHostFragment.navController
        val navInflater = navController.navInflater
        val graph = navInflater.inflate(R.navigation.navigation)
        val sectionId = intent.getIntExtra(KEY_SECTION_ID, -1)
        val bundle = bundleOf(KEY_SECTION_ID to sectionId)
        navHostFragment.navController.setGraph(graph, bundle)
class MainActivity : BaseActivity()  {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val navHostFragment = container as NavHostFragment
        val inflater = navHostFragment.navController.navInflater
        val graph = inflater.inflate(R.navigation.main_nav)

        var data : Data  = intent.getParcelableExtra("DATA") as Data
        var bundle : Bundle = Bundle()
        bundle.putParcelable("DATA", data)
        graph.addDefaultArguments(bundle)
        graph.addDefaultArguments(intent!!.extras!!)
        navHostFragment.navController.graph = graph

    }
}

Добавьте приведенный выше код в Activity для отправки данных с использованием навигации

override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        var data = NavHostFragment.findNavController(this).graph.defaultArguments.getParcelable("DATA") as Booking

}

Добавьте приведенный выше код в фрагменте

Я тоже не могу найти этот метод. Он не существует в документации по компонентам архитектуры.

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

// Kotlin Code, in Fragment
with(findNavController().graph) {
    get(startDestination).addArgument(...)
}