Android 导航组件:在片段中传递值(参数)
Android Navigation Component : Pass value (arguments) in fragments
我做了什么:
我创建了导航抽屉Activity,更新了导航抽屉Activity的新格式,作为每个新的 Android 架构,我用 Navigation Component 结构得到它。
下面是 NavController
和 NavigationUI
的 NavigationView
代码,点击任何导航项时的打开片段。
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_profile, R.id.nav_privacy_policy,
R.id.nav_terms, R.id.nav_contact_us, R.id.nav_share, R.id.nav_send)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
这是给 nav_host_fragment
的:
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
正在使用此进行导航 navigation/mobile_navigation.xml
<?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/mobile_navigation"
app:startDestination="@+id/nav_home">
<fragment
android:id="@+id/nav_home"
android:name="com.sohamerp.marsremedies.fragment.HomeFragment"
android:label="@string/menu_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/nav_profile"
android:name="com.sohamerp.marsremedies.fragment.ProfileFragment"
android:label="@string/menu_my_profile"
tools:layout="@layout/fragment_profile" />
<fragment
android:id="@+id/nav_privacy_policy"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="@string/menu_privacy_policy"
tools:layout="@layout/fragment_privacy_policy" />
<fragment
android:id="@+id/nav_terms"
android:name="com.sohamerp.marsremedies.fragment.TermsConditionFragment"
android:label="@string/menu_terms"
tools:layout="@layout/fragment_terms_condition" />
<fragment
android:id="@+id/nav_contact_us"
android:name="com.sohamerp.marsremedies.fragment.ContactUsFragment"
android:label="@string/menu_contact_us"
tools:layout="@layout/fragment_terms_condition" />
</navigation>
我想做的事情:
现在我想在 Fragment 被调用时将一些值作为一个包(参数)传递给它。
场景:我有两个片段 PrivacyPolicyFragment
和 TermsConditionsFragment
,在这两个片段中,我只是相应地在 WebView 中打开 links。所以当我点击Privacy Policy
的菜单项时,我会传递一个与之相关的link。
在这个新结构中navigation/mobile_navigation.xml
打开片段,我如何传递参数?
有什么帮助吗?
你可以实施 NavigationView.OnNavigationItemSelectedListener
并做这样的事情:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
drawer_layout.closeDrawers()
if (item.itemId == nv_navigation_drawer_navigation_view.checkedItem?.itemId)
return false
Handler().postDelayed({
when (item.itemId) {
R.id.nav_privacy_policy -> {
val action = FragmentDirections.actionFragmentToPrivacyFragment("Policy link")
findNavController().navigate(action)
}
}
}, DRAWER_NAVIGATION_DELAY)
return true
}
并且在 xml 中,您可以向接收片段添加参数,在本例中为
<fragment
android:id="@+id/nav_privacy_policy"
android:name=".fragment.PrivacyPolicyFragment"
android:label="@string/menu_privacy_policy"
tools:layout="@layout/fragment_privacy_policy">
<argument
android:name="policy"
app:argType="string" />
</fragment>
要将参数传递给其他 Fragments/Destinations,请使用确保类型安全的 Safe Args。就像@bromden 说明的那样,Safe Args 将为每个 fragment/destination 生成一个 class,其中 action
起源。然后,您可以将参数传递到导航到片段的 action
。
在接收片段中,说 PrivacyFragment
如果您的代码在 Kotlin 中,请使用 navArgs()
属性 委托来访问参数。即
val args: PrivacyFragmentArgs by navArgs()
要更好地理解这一点,请访问 Pass data between destinations
所以我忘了完成这个 link : Define Destination Arguments
但是这个答案对像我这样懒惰的人很有帮助:
在项目级别添加依赖build.gradle:
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0"
在应用级别应用插件build.gradle:
apply plugin: "androidx.navigation.safeargs"
使用XML:预定义(静态)值:
在导航的xml文件中/navigation/mobile_navigation.xml声明argument
标签如下,也可以通过this link:
<fragment
android:id="@+id/nav_privacy_policy"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="@string/menu_privacy_policy"
tools:layout="@layout/fragment_privacy_policy" >
<argument
android:name="privacyPolicyLink"
app:argType="string"
android:defaultValue="http://sohamerp.com/avo/avo_privacy_policy.html"/>
</fragment>
<fragment
android:id="@+id/nav_terms"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="@string/menu_terms"
tools:layout="@layout/fragment_terms_condition" >
<argument
android:name="privacyPolicyLink"
app:argType="string"
android:defaultValue="http://sohamerp.com/avo/avo_privacy_policy.html"/>
</fragment>
现在您必须在 Fragment 中编写代码,例如:
if(getArguments() != null) {
// The getPrivacyPolicyLink() method will be created automatically.
String url = PrivacyPolicyFragmentArgs.fromBundle(getArguments()).getPrivacyPolicyLink();
}
希望对其他人有所帮助。
您还可以传递可序列化对象、枚举值和原始类型数组。
例如:
enum class ObjectType : Serializable {
FIRST, SECOND
}
然后,将参数添加到 xml
<fragment
android:id="@+id/nav_profile"
android:name="com.sohamerp.marsremedies.fragment.ProfileFragment"
android:label="@string/menu_my_profile"
tools:layout="@layout/fragment_profile" >
<argument
android:name="myObjectType"
android:defaultValue="SECOND"
app:argType="com.project.app.data.ObjectType" />
</fragment>
注意,一定要指定完整路径!
在这种情况下,您可以使用
private NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
// Create the Bundle to pass, you can put String, Integer, or serializable object
Bundle bundle = new Bundle();
bundle.putString("link","http://yourlink.com/policy");
bundle.putSerializable("USER", user); // Serializable Object
navController.navigate(R.id.nav_terms, bundle); // called fragment with agruments
有什么帮助可以在上面回复
在Android Studio 3.2+ 的较新版本中,下面的依赖项和插件需要在build.gradle 文件
中添加
第一步
在项目级别中添加依赖项 build.gradle
dependencies {
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5'
}
在应用级应用插件 build.gradle
plugins {
id 'androidx.navigation.safeargs'
}
第 2 步
- 在导航文件中,res/navigation/nav_graph.xml
- 在任何 片段 或带有 action 标签[=63] 的内部片段中声明 argument 标签=]
- 列表项
Sample xml code
<fragment
android:id="@+id/nav_register"
android:name="com.pd.demo.ui.profile.RegisterFragment"
android:label="@string/title_register"
tools:layout="@layout/fragment_register">
<action
android:id="@+id/action_nav_register_to_nav_verify_otp"
app:destination="@id/nav_verify_otp">
<argument
android:name="mobile"
app:argType="string" />
<argument
android:name="password"
app:argType="string" />
</action>
</fragment>
第三步
在 Kotlin 代码下方,将参数传递给目标片段
val bundle = bundleOf("mobile" to binding.etMobileNo.text.toString().trim())
Navigation.findNavController(binding.root).navigate(R.id.action_nav_register_to_nav_verify_otp, bundle)
第四步
在 Kotlin 代码下方,从源片段获取包参数
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mobileNo = arguments!!.getString("mobile").toString()
password = arguments!!.getString("password").toString()
}
这段代码会有所帮助
简单快速的解决方案:
在目标之间传递参数
Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
正在接收
TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));
我遇到了同样的问题,但我仍然无法使用片段方向传递参数。因为我需要我的几个片段中的值,所以我决定在我的主 activity 中使用一个伴生对象。它可能不是最好的,但它解决了问题:
class 主活动:AppCompatActivity() {
companion object{
var myGlobalVar = "Example"
}
override fun onCreate(savedInstanceState: Bundle?) {....
然后我可以通过导入它在我的所有片段中访问它的值:
import myAppPackage.MainActivity.Companion.myGlobalVar
我不得不从我的 navGraph 中删除参数,但我仍然可以在后台访问它。
使用 NavController NavGraph 导航从起始目的地传递数据非常简单。我用它来显示与订单关联的订单行 header:
private void showRepositionLinesFragment(AppObjects.RepOrderHeader orderHeader) {
int number = orderHeader.getOrderNumber();
String orderNumber = String.format("%06d",number);
String createDate = orderHeader.getCreateDate();
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,
"Navigate to FragRepoLines with orderNumber: " + orderNumber,false);
NavController navController = NavHostFragment.findNavController(FragmentRepositionHeaders.this);
Bundle bundle = new Bundle();
bundle.putString(getString(R.string.arg_header_ordernumber),orderNumber);
bundle.putString(getString(R.string.arg_repheader_createdate), createDate);
navController.getGraph().findNode(R.id.FragRepoLines).setLabel(orderNumber + " " + createDate);
navController.navigate(R.id.action_FragRepoHeaders_to_FragRepoLines,bundle);
}
从处理订单行的片段中获取数据变得更加复杂。使用 NavController getArguments() 尝试了几个小时。
最后,这对我有用。
在起始片段中:
NavController navController = NavHostFragment.findNavController(this);
// We use a String here, but any type that can be put in a Bundle is supported
MutableLiveData<String> liveData = navController.getCurrentBackStackEntry()
.getSavedStateHandle()
.getLiveData(getString(R.string.arg_header_ordernumber));
liveData.observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(String s) {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info, "+++++++++ liveData changed -> " + s, false);
}
});
在目标片段中:
String arg = getString(R.string.arg_header_ordernumber);
NavController navController = NavHostFragment.findNavController(this);
NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
if (navBackStackEntry != null) {
SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
if (savedStateHandle != null) {
savedStateHandle.set(arg, "000000");
} else {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,"savedStateHandle == null",false);
}
} else {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,"navBackStackEntry == null",false);
}
来源:Interact programmatically with the Navigation component
我将 navController.getPreviousBackStackEntry() 更改为 navController.getCurrentBackStackEntry()
我做了什么:
我创建了导航抽屉Activity,更新了导航抽屉Activity的新格式,作为每个新的 Android 架构,我用 Navigation Component 结构得到它。
下面是 NavController
和 NavigationUI
的 NavigationView
代码,点击任何导航项时的打开片段。
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_profile, R.id.nav_privacy_policy,
R.id.nav_terms, R.id.nav_contact_us, R.id.nav_share, R.id.nav_send)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
这是给 nav_host_fragment
的:
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
正在使用此进行导航 navigation/mobile_navigation.xml
<?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/mobile_navigation"
app:startDestination="@+id/nav_home">
<fragment
android:id="@+id/nav_home"
android:name="com.sohamerp.marsremedies.fragment.HomeFragment"
android:label="@string/menu_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/nav_profile"
android:name="com.sohamerp.marsremedies.fragment.ProfileFragment"
android:label="@string/menu_my_profile"
tools:layout="@layout/fragment_profile" />
<fragment
android:id="@+id/nav_privacy_policy"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="@string/menu_privacy_policy"
tools:layout="@layout/fragment_privacy_policy" />
<fragment
android:id="@+id/nav_terms"
android:name="com.sohamerp.marsremedies.fragment.TermsConditionFragment"
android:label="@string/menu_terms"
tools:layout="@layout/fragment_terms_condition" />
<fragment
android:id="@+id/nav_contact_us"
android:name="com.sohamerp.marsremedies.fragment.ContactUsFragment"
android:label="@string/menu_contact_us"
tools:layout="@layout/fragment_terms_condition" />
</navigation>
我想做的事情:
现在我想在 Fragment 被调用时将一些值作为一个包(参数)传递给它。
场景:我有两个片段 PrivacyPolicyFragment
和 TermsConditionsFragment
,在这两个片段中,我只是相应地在 WebView 中打开 links。所以当我点击Privacy Policy
的菜单项时,我会传递一个与之相关的link。
在这个新结构中navigation/mobile_navigation.xml
打开片段,我如何传递参数?
有什么帮助吗?
你可以实施 NavigationView.OnNavigationItemSelectedListener
并做这样的事情:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
drawer_layout.closeDrawers()
if (item.itemId == nv_navigation_drawer_navigation_view.checkedItem?.itemId)
return false
Handler().postDelayed({
when (item.itemId) {
R.id.nav_privacy_policy -> {
val action = FragmentDirections.actionFragmentToPrivacyFragment("Policy link")
findNavController().navigate(action)
}
}
}, DRAWER_NAVIGATION_DELAY)
return true
}
并且在 xml 中,您可以向接收片段添加参数,在本例中为
<fragment
android:id="@+id/nav_privacy_policy"
android:name=".fragment.PrivacyPolicyFragment"
android:label="@string/menu_privacy_policy"
tools:layout="@layout/fragment_privacy_policy">
<argument
android:name="policy"
app:argType="string" />
</fragment>
要将参数传递给其他 Fragments/Destinations,请使用确保类型安全的 Safe Args。就像@bromden 说明的那样,Safe Args 将为每个 fragment/destination 生成一个 class,其中 action
起源。然后,您可以将参数传递到导航到片段的 action
。
在接收片段中,说 PrivacyFragment
如果您的代码在 Kotlin 中,请使用 navArgs()
属性 委托来访问参数。即
val args: PrivacyFragmentArgs by navArgs()
要更好地理解这一点,请访问 Pass data between destinations
所以我忘了完成这个 link : Define Destination Arguments
但是这个答案对像我这样懒惰的人很有帮助:
在项目级别添加依赖build.gradle:
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0"
在应用级别应用插件build.gradle:
apply plugin: "androidx.navigation.safeargs"
使用XML:预定义(静态)值:
在导航的xml文件中/navigation/mobile_navigation.xml声明argument
标签如下,也可以通过this link:
<fragment
android:id="@+id/nav_privacy_policy"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="@string/menu_privacy_policy"
tools:layout="@layout/fragment_privacy_policy" >
<argument
android:name="privacyPolicyLink"
app:argType="string"
android:defaultValue="http://sohamerp.com/avo/avo_privacy_policy.html"/>
</fragment>
<fragment
android:id="@+id/nav_terms"
android:name="com.sohamerp.marsremedies.fragment.PrivacyPolicyFragment"
android:label="@string/menu_terms"
tools:layout="@layout/fragment_terms_condition" >
<argument
android:name="privacyPolicyLink"
app:argType="string"
android:defaultValue="http://sohamerp.com/avo/avo_privacy_policy.html"/>
</fragment>
现在您必须在 Fragment 中编写代码,例如:
if(getArguments() != null) {
// The getPrivacyPolicyLink() method will be created automatically.
String url = PrivacyPolicyFragmentArgs.fromBundle(getArguments()).getPrivacyPolicyLink();
}
希望对其他人有所帮助。
您还可以传递可序列化对象、枚举值和原始类型数组。 例如:
enum class ObjectType : Serializable {
FIRST, SECOND
}
然后,将参数添加到 xml
<fragment
android:id="@+id/nav_profile"
android:name="com.sohamerp.marsremedies.fragment.ProfileFragment"
android:label="@string/menu_my_profile"
tools:layout="@layout/fragment_profile" >
<argument
android:name="myObjectType"
android:defaultValue="SECOND"
app:argType="com.project.app.data.ObjectType" />
</fragment>
注意,一定要指定完整路径!
在这种情况下,您可以使用
private NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
// Create the Bundle to pass, you can put String, Integer, or serializable object
Bundle bundle = new Bundle();
bundle.putString("link","http://yourlink.com/policy");
bundle.putSerializable("USER", user); // Serializable Object
navController.navigate(R.id.nav_terms, bundle); // called fragment with agruments
有什么帮助可以在上面回复
在Android Studio 3.2+ 的较新版本中,下面的依赖项和插件需要在build.gradle 文件
中添加第一步
在项目级别中添加依赖项 build.gradle
dependencies {
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5'
}
在应用级应用插件 build.gradle
plugins {
id 'androidx.navigation.safeargs'
}
第 2 步
- 在导航文件中,res/navigation/nav_graph.xml
- 在任何 片段 或带有 action 标签[=63] 的内部片段中声明 argument 标签=]
- 列表项
Sample xml code
<fragment
android:id="@+id/nav_register"
android:name="com.pd.demo.ui.profile.RegisterFragment"
android:label="@string/title_register"
tools:layout="@layout/fragment_register">
<action
android:id="@+id/action_nav_register_to_nav_verify_otp"
app:destination="@id/nav_verify_otp">
<argument
android:name="mobile"
app:argType="string" />
<argument
android:name="password"
app:argType="string" />
</action>
</fragment>
第三步
在 Kotlin 代码下方,将参数传递给目标片段
val bundle = bundleOf("mobile" to binding.etMobileNo.text.toString().trim())
Navigation.findNavController(binding.root).navigate(R.id.action_nav_register_to_nav_verify_otp, bundle)
第四步
在 Kotlin 代码下方,从源片段获取包参数
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mobileNo = arguments!!.getString("mobile").toString()
password = arguments!!.getString("password").toString()
}
这段代码会有所帮助
简单快速的解决方案:
在目标之间传递参数
Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
正在接收
TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));
我遇到了同样的问题,但我仍然无法使用片段方向传递参数。因为我需要我的几个片段中的值,所以我决定在我的主 activity 中使用一个伴生对象。它可能不是最好的,但它解决了问题: class 主活动:AppCompatActivity() {
companion object{
var myGlobalVar = "Example"
}
override fun onCreate(savedInstanceState: Bundle?) {....
然后我可以通过导入它在我的所有片段中访问它的值:
import myAppPackage.MainActivity.Companion.myGlobalVar
我不得不从我的 navGraph 中删除参数,但我仍然可以在后台访问它。
使用 NavController NavGraph 导航从起始目的地传递数据非常简单。我用它来显示与订单关联的订单行 header:
private void showRepositionLinesFragment(AppObjects.RepOrderHeader orderHeader) {
int number = orderHeader.getOrderNumber();
String orderNumber = String.format("%06d",number);
String createDate = orderHeader.getCreateDate();
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,
"Navigate to FragRepoLines with orderNumber: " + orderNumber,false);
NavController navController = NavHostFragment.findNavController(FragmentRepositionHeaders.this);
Bundle bundle = new Bundle();
bundle.putString(getString(R.string.arg_header_ordernumber),orderNumber);
bundle.putString(getString(R.string.arg_repheader_createdate), createDate);
navController.getGraph().findNode(R.id.FragRepoLines).setLabel(orderNumber + " " + createDate);
navController.navigate(R.id.action_FragRepoHeaders_to_FragRepoLines,bundle);
}
从处理订单行的片段中获取数据变得更加复杂。使用 NavController getArguments() 尝试了几个小时。 最后,这对我有用。
在起始片段中:
NavController navController = NavHostFragment.findNavController(this);
// We use a String here, but any type that can be put in a Bundle is supported
MutableLiveData<String> liveData = navController.getCurrentBackStackEntry()
.getSavedStateHandle()
.getLiveData(getString(R.string.arg_header_ordernumber));
liveData.observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(String s) {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info, "+++++++++ liveData changed -> " + s, false);
}
});
在目标片段中:
String arg = getString(R.string.arg_header_ordernumber);
NavController navController = NavHostFragment.findNavController(this);
NavBackStackEntry navBackStackEntry = navController.getCurrentBackStackEntry();
if (navBackStackEntry != null) {
SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
if (savedStateHandle != null) {
savedStateHandle.set(arg, "000000");
} else {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,"savedStateHandle == null",false);
}
} else {
Globals.LogTrace(this, AppAlertDialog.DialogType.Info,"navBackStackEntry == null",false);
}
来源:Interact programmatically with the Navigation component
我将 navController.getPreviousBackStackEntry() 更改为 navController.getCurrentBackStackEntry()