使用 NavController 从一个活动导航到另一个活动
Posted
技术标签:
【中文标题】使用 NavController 从一个活动导航到另一个活动【英文标题】:Navigate from one activity to another with a NavController 【发布时间】:2021-09-12 23:01:32 【问题描述】:我有两项活动,一项使用导航图,另一项不使用。如何从不使用 NavController 的活动导航到导航图中的片段?
我正在尝试从 ImportMonsterActivity(将新实体添加到数据库后)导航到 MainActivity 导航图中的 EditMonsterFragment。
我想我应该能够创建一个正常的意图并给它一些额外的东西来指定导航图中的位置,但我还没有找到任何关于这种导航的文档。一切要么使用来自另一个应用程序的深层链接,要么在导航图中导航。
如果我必须向我的图表添加深层链接,我可以不使用 http 来执行此操作吗?如果可能的话,我不希望这个应用程序需要互联网访问。我希望人们能够将他们下载或复制的文件导入设备。
androidManifest.xml
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<nav-graph android:value="@navigation/mobile_navigation" />
</activity>
<activity
android:name=".ImportMonsterActivity"
android:icon="@mipmap/ic_launcher"
android:label="Import Monster"
android:launchMode="singleTask"
android:priority="50">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
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/navigation_dashboard">
<fragment
android:id="@+id/navigation_search"
android:name="com.majinnaibu.monstercards.ui.search.SearchFragment"
android:label="@string/title_search"
tools:layout="@layout/fragment_search">
<action
android:id="@+id/action_navigation_search_to_navigation_monster"
app:destination="@id/navigation_monster" />
</fragment>
<fragment
android:id="@+id/navigation_dashboard"
android:name="com.majinnaibu.monstercards.ui.dashboard.DashboardFragment"
android:label="@string/title_dashboard"
tools:layout="@layout/fragment_dashboard">
<action
android:id="@+id/action_navigation_dashboard_to_navigation_monster"
app:destination="@id/navigation_monster" />
</fragment>
<fragment
android:id="@+id/navigation_collections"
android:name="com.majinnaibu.monstercards.ui.collections.CollectionsFragment"
android:label="@string/title_collections"
tools:layout="@layout/fragment_collections">
<action
android:id="@+id/action_navigation_collections_to_navigation_monster"
app:destination="@id/navigation_monster" />
</fragment>
<fragment
android:id="@+id/navigation_library"
android:name="com.majinnaibu.monstercards.ui.library.LibraryFragment"
android:label="@string/title_library"
tools:layout="@layout/fragment_library">
<action
android:id="@+id/action_navigation_library_to_navigation_monster"
app:destination="@id/navigation_monster" />
</fragment>
<fragment
android:id="@+id/navigation_monster"
android:name="com.majinnaibu.monstercards.ui.monster.MonsterDetailFragment"
android:label="Monster"
tools:layout="@layout/fragment_monster">
<argument
android:name="monster_id"
app:argType="string" />
<action
android:id="@+id/action_navigation_monster_to_editMonsterFragment"
app:destination="@id/edit_monster_navigation" />
</fragment>
<navigation
android:id="@+id/edit_monster_navigation"
app:startDestination="@id/editMonsterFragment">
<argument
android:name="monster_id"
app:argType="string" />
<fragment
android:id="@+id/editMonsterFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditMonsterFragment"
android:label="Edit Monster"
tools:layout="@layout/fragment_edit_monster">
<argument
android:name="monster_id"
app:argType="string" />
<action
android:id="@+id/action_editMonsterFragment_to_editBasicInfoFragment"
app:destination="@id/editBasicInfoFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editArmorFragment"
app:destination="@id/editArmorFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editSpeedFragment"
app:destination="@id/editSpeedFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editAbilityScoresFragment"
app:destination="@id/editAbilityScoresFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editSavingThrowsFragment"
app:destination="@id/editSavingThrowsFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editChallengeRatingFragment"
app:destination="@id/editChallengeRatingFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editSkillsFragment"
app:destination="@id/editSkillsFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editLanguagesFragment"
app:destination="@id/editLanguagesFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editTraitListFragment"
app:destination="@id/editTraitListFragment" />
<action
android:id="@+id/action_editMonsterFragment_to_editStringsFragment"
app:destination="@id/editStringsFragment" />
</fragment>
<fragment
android:id="@+id/editBasicInfoFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditBasicInfoFragment"
android:label="fragment_edit_basic_info"
tools:layout="@layout/fragment_edit_basic_info" />
<fragment
android:id="@+id/editArmorFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditArmorFragment"
android:label="fragment_edit_armor"
tools:layout="@layout/fragment_edit_armor" />
<fragment
android:id="@+id/editSpeedFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditSpeedFragment"
android:label="fragment_edit_speed"
tools:layout="@layout/fragment_edit_speed" />
<fragment
android:id="@+id/editAbilityScoresFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditAbilityScoresFragment"
android:label="EditAbilityScoresFragment" />
<fragment
android:id="@+id/editSavingThrowsFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditSavingThrowsFragment"
android:label="fragment_edit_saving_throws"
tools:layout="@layout/fragment_edit_saving_throws" />
<fragment
android:id="@+id/editChallengeRatingFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditChallengeRatingFragment"
android:label="fragment_edit_challenge_rating"
tools:layout="@layout/fragment_edit_challenge_rating" />
<fragment
android:id="@+id/editSkillsFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditSkillsFragment"
android:label="fragment_edit_skills_list"
tools:layout="@layout/fragment_edit_skills_list">
<action
android:id="@+id/action_editSkillsFragment_to_editSkillFragment"
app:destination="@id/editSkillFragment" />
</fragment>
<fragment
android:id="@+id/editSkillFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditSkillFragment"
android:label="fragment_edit_skill"
tools:layout="@layout/fragment_edit_skill">
<argument
android:name="name"
app:argType="string" />
<argument
android:name="abilityScore"
app:argType="com.majinnaibu.monstercards.data.enums.AbilityScore" />
<argument
android:name="proficiency"
app:argType="com.majinnaibu.monstercards.data.enums.ProficiencyType" />
<argument
android:name="advantage"
app:argType="com.majinnaibu.monstercards.data.enums.AdvantageType" />
</fragment>
<fragment
android:id="@+id/editLanguagesFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditLanguagesFragment"
android:label="fragment_edit_languages_list"
tools:layout="@layout/fragment_edit_languages_list">
<action
android:id="@+id/action_editLanguagesFragment_to_editLanguageFragment"
app:destination="@id/editLanguageFragment" />
</fragment>
<fragment
android:id="@+id/editLanguageFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditLanguageFragment"
android:label="fragment_edit_language"
tools:layout="@layout/fragment_edit_language">
<argument
android:name="name"
app:argType="string" />
<argument
android:name="canSpeak"
app:argType="boolean" />
</fragment>
<fragment
android:id="@+id/editTraitFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditTraitFragment"
android:label="EditTraitFragment">
<argument
android:name="description"
app:argType="string" />
<argument
android:name="name"
app:argType="string" />
<argument
android:name="traitType"
app:argType="com.majinnaibu.monstercards.data.enums.TraitType" />
</fragment>
<fragment
android:id="@+id/editTraitListFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditTraitsFragment"
android:label="EditTraitListFragment">
<action
android:id="@+id/action_editTraitListFragment_to_editTraitFragment"
app:destination="@id/editTraitFragment" />
<argument
android:name="traitType"
app:argType="com.majinnaibu.monstercards.data.enums.TraitType" />
</fragment>
<fragment
android:id="@+id/editStringsFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditStringsFragment"
android:label="EditStringsFragment">
<action
android:id="@+id/action_editStringsFragment_to_editStringFragment"
app:destination="@id/editStringFragment" />
<argument
android:name="stringType"
app:argType="com.majinnaibu.monstercards.data.enums.StringType" />
</fragment>
<fragment
android:id="@+id/editStringFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditStringFragment"
android:label="EditStringFragment">
<argument
android:name="stringType"
app:argType="com.majinnaibu.monstercards.data.enums.StringType" />
<argument
android:name="value"
app:argType="string" />
</fragment>
</navigation>
</navigation>
ImportMonsterActivity 中的这个方法让我在 ImportMonsterActivity 的新实例中没有参数/附加/参数。
private void navigateToEditMonster(UUID monsterId)
Logger.logUnimplementedFeature(String.format("navigate to editing the monster %s", monsterId));
NavDeepLinkBuilder builder = new NavDeepLinkBuilder(this);
Bundle args = new Bundle();
args.putString("monster_id", monsterId.toString());
PendingIntent pi = builder.setGraph(R.navigation.mobile_navigation).setDestination(R.id.edit_monster_navigation).setArguments(args).createPendingIntent();
try
pi.send(); // This line is executed
catch (PendingIntent.CanceledException e)
e.printStackTrace(); // This exception is not thrown
更新:我尝试用导航图中的片段替换第二个活动,并添加一个深层链接以打开它以进行共享和查看操作,但除非我为深层链接提供@987654325,否则我会遇到构建错误@。当我设置 uri 时,当意图尝试打开我的活动时,应用程序崩溃。
没有 uri 的构建错误
Execution failed for task ':app:extractDeepLinksDebug'.
> Navigation XML document <deepLink> element must contain a app:uri attribute.
使用空 uri 构建错误
Execution failed for task ':app:extractDeepLinksDebug'.
> java.net.URISyntaxException: Expected authority at index 2: //
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/navigation_dashboard">
<!-- unrelated fragments -->
<fragment
android:id="@+id/navigation_library"
android:name="com.majinnaibu.monstercards.ui.library.LibraryFragment"
android:label="@string/title_library"
tools:layout="@layout/fragment_library">
<action
android:id="@+id/action_navigation_library_to_navigation_monster"
app:destination="@id/navigation_monster" />
</fragment>
<fragment
android:id="@+id/navigation_monster"
android:name="com.majinnaibu.monstercards.ui.monster.MonsterDetailFragment"
android:label="Monster"
tools:layout="@layout/fragment_monster">
<argument
android:name="monster_id"
app:argType="string" />
<action
android:id="@+id/action_navigation_monster_to_editMonsterFragment"
app:destination="@id/edit_monster_navigation" />
</fragment>
<navigation
android:id="@+id/edit_monster_navigation"
app:startDestination="@id/editMonsterFragment">
<argument
android:name="monster_id"
app:argType="string" />
<fragment
android:id="@+id/editMonsterFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditMonsterFragment"
android:label="Edit Monster"
tools:layout="@layout/fragment_edit_monster">
<argument
android:name="monster_id"
app:argType="string" />
<action
android:id="@+id/action_editMonsterFragment_to_editBasicInfoFragment"
app:destination="@id/editBasicInfoFragment" />
<!-- other actions here to navigate to fragments in this sub graph -->
</fragment>
<!-- other fragments here -->
</navigation>
<fragment
android:id="@+id/monsterImportFragment"
android:name="com.majinnaibu.monstercards.ui.monster.MonsterImportFragment"
android:label="MonsterImportFragment"
tools:layout="@layout/fragment_monster">
<action
android:id="@+id/action_monsterImportFragment_to_edit_monster_navigation"
app:destination="@id/edit_monster_navigation" />
<deepLink
android:id="@+id/deepLink2"
app:action="ACTION_VIEW"
app:mimeType="application/octet-stream" />
</fragment>
</navigation>
如果我用这个替换该深层链接,则应用在加载匹配的操作和 mimeType 时会崩溃。
<deepLink
android:id="@+id/deepLink2"
app:action="ACTION_VIEW"
app:mimeType="application/octet-stream"
app:uri="app://import-monster" />
设置应用程序时出错:uri
06-30 13:41:52.004 19299 19299 E AndroidRuntime: Process: com.majinnaibu.monstercards, PID: 19299
06-30 13:41:52.004 19299 19299 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfocom.majinnaibu.monstercards/com.majinnaibu.monstercards.MainActivity: android.view.InflateException: Binary XML file line #33 in com.majinnaibu.monstercards:layout/activity_main: Binary XML file line #33 in com.majinnaibu.monstercards:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
06-30 13:41:52.004 19299 19299 E AndroidRuntime: Caused by: android.view.InflateException: Binary XML file line #33 in com.majinnaibu.monstercards:layout/activity_main: Binary XML file line #33 in com.majinnaibu.monstercards:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
06-30 13:41:52.004 19299 19299 E AndroidRuntime: Caused by: android.view.InflateException: Binary XML file line #33 in com.majinnaibu.monstercards:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
06-30 13:41:52.005 19299 19299 E AndroidRuntime: at com.majinnaibu.monstercards.MainActivity.onCreate(MainActivity.java:34)
06-30 13:41:52.009 1367 2360 W ActivityManager: crash : com.majinnaibu.monstercards,0
06-30 13:41:52.010 1367 2360 W ActivityTaskManager: Force finishing activity com.majinnaibu.monstercards/.MainActivity
06-30 13:41:52.012 1367 2360 W ActivityTaskManager: Force finishing activity com.majinnaibu.monstercards/.MainActivity
【问题讨论】:
为什么ImportMonsterActivity
首先是一个单独的活动?自 Navigation 2.3.0 起,导航支持操作/mimeTypes 的深层链接。
我尝试在导航图中添加一个新片段以进行导入,并创建了深层链接,但我不知道将 app:uri 设置为什么。我希望用户能够打开任何文本/纯文本或应用程序/八位字节流进行导入。这就是我选择独立活动的原因。
如果您添加了app:action
/app:mimeType
,导航不需要app:uri
。是什么让您认为您需要app:uri
?
我收到一个构建错误,告诉我我需要包含一个。 Execution failed for task ':app:extractDeepLinksDebug'. > Navigation XML document <deepLink> element must contain a app:uri attribute.
【参考方案1】:
这是一种解决方法,因为我找不到答案。我希望我能及时找到更好的解决方案。我不会将此标记为答案,因为它是一种变通方法并且不会回答问题。
我创建了一个片段来替换单独的活动,但我无法获得指向它的深层链接以响应相同的意图 action=android.intent.action.VIEW
和 mimeType=text/plain
或 application/octet-stream
相反我处理 MainActivity 的 onNewIntent(Intent intent)
方法中的意图,如果我识别出导入意图,我将导航到那里的导入片段。这让我可以正常使用 NavigationController 在导入片段中导航。
我仍然认为这是应该工作的东西,但我还没有弄清楚如何,但对于其他想要做类似事情的人,我唯一的建议是重新设计你的解决方案以不需要它。
在我的清单中,我将意图过滤器移动到我的 MainActivity 并将其 launchMode 设置为单个任务。 AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.majinnaibu.monstercards">
<application
android:name=".MonsterCardsApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.PICK" />
<action android:name="android.intent.action.INSERT" />
<action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.ALTERNATIVE" />
<category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:mimeType="text/plain"
android:scheme="content" />
<data
android:mimeType="application/octet-stream"
android:scheme="content" />
<data
android:mimeType="text/plain"
android:scheme="file" />
</intent-filter>
<nav-graph android:value="@navigation/mobile_navigation" />
</activity>
</application>
</manifest>
MainActivity.java
public class MainActivity extends AppCompatActivity
@Override
protected void onNewIntent(Intent intent)
super.onNewIntent(intent);
String json = readMonsterJSONFromIntent(intent);
if (!StringHelper.isNullOrEmpty(json))
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
NavDirections action = MobileNavigationDirections.actionGlobalMonsterImportFragment(json);
navController.navigate(action);
private String readMonsterJSONFromIntent(Intent intent)
String action = intent.getAction();
Bundle extras = intent.getExtras();
String type = intent.getType();
String json;
Uri uri = null;
if ("android.intent.action.SEND".equals(action) && "text/plain".equals(type))
uri = extras.getParcelable("android.intent.extra.STREAM");
else if ("android.intent.action.VIEW".equals(action) && ("text/plain".equals(type) || "application/octet-stream".equals(type)))
uri = intent.getData();
else
Logger.logError(String.format("unexpected launch configuration action: %s, type: %s, uri: %s", action, type, uri));
if (uri == null)
return null;
json = readContentsOfUri(uri);
if (StringHelper.isNullOrEmpty(json))
return null;
return json;
private String readContentsOfUri(Uri uri)
StringBuilder builder = new StringBuilder();
try (InputStream inputStream =
getContentResolver().openInputStream(uri);
BufferedReader reader = new BufferedReader(
new InputStreamReader(Objects.requireNonNull(inputStream))))
String line;
while ((line = reader.readLine()) != null)
builder.append(line);
catch (IOException e)
Logger.logError("error reading file", e);
return null;
return builder.toString();
在我的移动导航中,我为我的导入片段创建了一个全局动作,以及一个从导入片段到堆栈中我想要的下一个片段的动作。我在这里删除了我的 nav xml 中不相关的部分。
mobile_navigation.xml
<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/navigation_dashboard">
<fragment
android:id="@+id/navigation_library"
android:name="com.majinnaibu.monstercards.ui.library.LibraryFragment"
android:label="@string/title_library"
tools:layout="@layout/fragment_library">
<action
android:id="@+id/action_navigation_library_to_navigation_monster"
app:destination="@id/navigation_monster" />
</fragment>
<fragment
android:id="@+id/navigation_monster"
android:name="com.majinnaibu.monstercards.ui.monster.MonsterDetailFragment"
android:label="Monster"
tools:layout="@layout/fragment_monster">
<argument
android:name="monster_id"
app:argType="string" />
<action
android:id="@+id/action_navigation_monster_to_editMonsterFragment"
app:destination="@id/edit_monster_navigation" />
</fragment>
<navigation
android:id="@+id/edit_monster_navigation"
app:startDestination="@id/editMonsterFragment">
<argument
android:name="monster_id"
app:argType="string" />
<fragment
android:id="@+id/editMonsterFragment"
android:name="com.majinnaibu.monstercards.ui.editmonster.EditMonsterFragment"
android:label="Edit Monster"
tools:layout="@layout/fragment_edit_monster">
<argument
android:name="monster_id"
app:argType="string" />
</fragment>
</navigation>
<fragment
android:id="@+id/monsterImportFragment"
android:name="com.majinnaibu.monstercards.ui.monster.MonsterImportFragment"
android:label="MonsterImportFragment"
tools:layout="@layout/fragment_monster">
<argument
android:name="json"
app:argType="string" />
<action
android:id="@+id/action_monsterImportFragment_to_navigation_library"
app:destination="@id/navigation_library"
app:popUpTo="@id/monsterImportFragment"
app:popUpToInclusive="true" />
</fragment>
<action
android:id="@+id/action_global_monsterImportFragment"
app:destination="@id/monsterImportFragment" />
</navigation>
在我的导入片段中,我使用这种方法来设置我想要的后堆栈并导航到我的编辑片段。我不知道另一种方法可以在不知道前一个片段是什么的情况下从堆栈中删除当前片段并设置我想要的完整堆栈,而无需浏览所有片段以将它们推入堆栈。
MonsterImportFragment.java
private void navigateToEditMonster(UUID monsterId)
NavController navController = Navigation.findNavController(requireView());
NavDirections action;
action = MonsterImportFragmentDirections.actionMonsterImportFragmentToNavigationLibrary();
navController.navigate(action);
action = LibraryFragmentDirections.actionNavigationLibraryToNavigationMonster(monsterId.toString());
navController.navigate(action);
action = MonsterDetailFragmentDirections.actionNavigationMonsterToEditMonsterFragment(monsterId.toString());
navController.navigate(action);
编辑:我认为不幸的是没有办法做到这一点。我仍然认为该功能是一个完全有效的问题。我认为,如果您在主活动中为您想要链接的内容创建一个深层链接并在不使用导航控制器的情况下导航到该内容,这是可能的。
【讨论】:
以上是关于使用 NavController 从一个活动导航到另一个活动的主要内容,如果未能解决你的问题,请参考以下文章