Компонент архитектуры навигации - передача данных аргумента в startDestination
У меня есть действие A, которое запускает действие B и передает ему некоторые намеренные данные. Деятельность B содержит граф навигации из нового компонента архитектуры навигации. Я хочу передать эти данные намерения фрагменту startDestination в качестве аргумента, как это сделать?
12 ответов
Хорошо, я нашел решение этой проблемы благодаря Lan Lake из команды Google. Допустим, у вас есть действие A, которое запустит действие B с некоторыми намеренными данными, и вы хотите получить эти данные в startDestination, у вас есть два варианта здесь, если вы используете безопасные аргументы, что в моем случае вы могли бы сделать
StartFragmentArgs.fromBundle(requireActivity().intent?.extras)
читать аргументы из намерения. Если вы не используете безопасные аргументы, вы можете извлечь данные из пакета, который вы используете самостоятельно requireActivity().intent?.extras
который вернет пакет, который вы можете использовать вместо фрагмента getArguments()
метод. Вот и все, я пытаюсь, и все работает отлично.
Я думаю, что это изменилось снова с выпуском 1.0.0. И Google очень хорошо спрятал эту информацию в официальной документации. Или, по крайней мере, я изо всех сил пытался найти его, но наткнулся на него в руководстве по компоненту Миграция к навигации. О том, как передать аргументы в место назначения, упоминается здесь:
https://developer.android.com/guide/navigation/navigation-migrate
Короче
- Вы должны установить график навигации программно:
findNavController(R.id.main_content) .setGraph(R.navigation.product_detail_graph, intent.extras)
- Не устанавливайте график в объявлении NavHostFragment XML.
- Читайте дополнения со стороны получателя:
val args by navArgs<ProductDetailsArgs>() val productId = args.productId
Обновление: Google заявил, что официальная документация для передачи аргументов исходной цели навигации действительно отсутствует. Надеемся, что это будет добавлено в ближайшее время как часть документации по компоненту навигации.
TLDR: необходимо вручную накачать график, добавить ключи / значения в defaultArgs и установить график на 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
В действии, которое будет отображать ваш 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
Это было исправлено в 1.0.0-alpha07
, См детали.
Решение похоже на ответ Эллиота Шрока, но с использованием официального API.
Надо вручную надуть NavHostFragment
или же graph
использование
NavHostFragment.create(R.navigation.graph, args)
Или же
navController.setGraph(R.navigation.graph, args)
Аргументы - это данные, которые мы хотим передать в пункт назначения.
После передачи данных в раздел начального пункта назначения из официального документа:
Сначала создайте Bundle, который содержит данные:
val bundle = Bundle().apply {
putString(KEY, "value")
}
Затем используйте один из следующих методов, чтобы передать Bundle начальному месту назначения:
Если вы создаете свой NavHost программно
NavHostFragment.create(R.navigation.graph, bundle)
В противном случае вы можете установить аргументы пункта назначения, вызвав одну из следующих перегрузок
NavController.setGraph()
:navHostFragment.navController.setGraph(R.navigation.graph, bundle)
Тогда вам следует использовать Fragment.getArguments()
для получения данных в пункте назначения.
РЕДАКТИРОВАТЬ:
Вы также можете использовать FragmentArgs
вместо создания пакета вручную, что делает его более удобным и безопасным по типу:
navHostFragment.navController.setGraph(R.navigation.graph, MyFragmentArgs(arg).toBundle())
Затем во фрагменте вы можете получить аргументы как:
private val args: PodFragmentArgs by navArgs()
Убедитесь, что ваш фрагмент имеет элемент аргумента в файле navigation.xml:
<fragment
android:id="@+id/myFragment"
android:name="MyFragment"
android:label="fragment_my"
tools:layout="@layout/fragment_my">
<argument
android:name="argName"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
</fragment>
Итак, наконец, я нашел решение этой проблемы.
На момент написания этого ответа я использую 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
После прочтения решения я сделал тот, который подходит для моих нужд, это решение предполагает, что данные, отправленные в действие, на котором размещен этот график
в пункте назначения:
@Override
public void onAttach(Context context) {
super.onAttach(context);
// work around get get starting destination with activity bundle
userId = getActivity().getIntent().getIntExtra(KEY_USER_ID, -1);
}
Add DefaultArguments больше нет в последних версиях библиотеки. Я исправил проблему так:
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
Так что для людей все еще борющихся с этим. Я нашел другой способ сделать это, не используя Safe-Args и шаг, используя ответ @Elliot's.
Допустим, вы получили некоторые аргументы в Деятельности 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
Вы можете передавать данные в начальную точку назначения вашего приложения. Во-первых, вы должны явно создать Bundle, содержащий данные. Затем используйте один из следующих методов, чтобы передать Bundle в начальную точку назначения, которую вы можете установить вручную график из кода, добавить аргумент с ним и удалить
app:navGraph
из xml и используйте эту строку кода в действии
navController.setGraph(R.navigation.graph, args)
Я использовал ответ Эллиота Шрока, но добавил пакет другим методом.
navController.setGraph(R.navigation.nav_graph, intent.extras!!)
class HadithActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hadith)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragmentContainerView) as NavHostFragment
val navController = navHostFragment.navController
navController.setGraph(R.navigation.nav_hadith, intent.extras)
}
}
activity_hadith.xml
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_hadith" />