Архитектура навигации 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
Короче говоря
- Вы должны установить график навигации программно:
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 заявил, что официальная документация для передачи аргументов исходной цели навигации действительно отсутствует. Надеемся, что это будет добавлено в ближайшее время как часть документации по компоненту навигации.
Ответ 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
Я нашел решение после некоторых исследований. Он работает с последней версией библиотеки навигации. Обратитесь к приведенному ниже коду:
Добавьте это в свой макет деятельности. Примечание. Мы не устанавливаем аргумент 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" />
В своем 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);
}
}
Напишите навигационную диаграмму ниже и добавьте те же ключи аргументов в начальный фрагмент.
<?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>
В вашем фрагменте просто извлеките значения с помощью функции getArguments().
String name = getArguments().getString("your name key");
String mail = getArguments().getString("your mail key");