提问者:小点点

使用导航控制器从一个活动导航到另一个活动


我有两个活动,一个使用导航图,另一个不使用。如何从不使用导航控制器的活动导航到导航图中的片段?

我试图从ImportMonsterActivity(在将新实体添加到db之后)导航到MainActivity导航图中的EditMonsterFragment。

我想我应该能够创建一个正常的意图,并给它一些额外的东西来指定导航图中的位置,但是我没有找到这种导航的任何留档。一切要么使用另一个应用程序的深层链接,要么在导航图中导航。

如果我必须添加一个深度链接到我的图形,我可以不使用http吗?如果可能的话,我不希望这个应用程序需要互联网接入。我希望人们能够只导入他们下载或复制到设备上的文件。

anifest.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_导航。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实例中,没有参数/extras/args。

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
    }
}

更新:我尝试用导航图中的一个片段替换第二个活动,并添加一个深度链接以打开它以进行共享和查看操作,但我会遇到构建错误,除非我给深度链接一个app:uri。当我设置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_导航。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 ComponentInfo{com.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

共1个答案

匿名用户

这是一个变通方法,因为我找不到答案。我希望能及时找到更好的解决方案。我不会把这个标记为答案,因为这是一个变通方法,不会回答这个问题。

我创建了一个片段来替换单独的活动,但我无法获得到它的深层链接,以响应相同的意图操作=android。意图行动查看和mimeType=text/plain或应用程序/八位字节流相反,我在MainActivity的onnewinent(Intent-Intent)方法中处理意图,如果我识别出导入意图,我将导航到那里的导入片段。这让我可以正常使用NavigationController在导入片段内导航。

我仍然认为这是应该工作的东西,我只是还没有弄清楚如何工作,但是对于任何其他想做类似事情的人,我唯一的建议是重新设计你的解决方案,不需要这个。

在我的清单中,我将意向过滤器移动到我的主活动,并将其启动模式设置为单个任务。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_导航。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);
}

编辑:不幸的是,我认为没有办法做到这一点。我仍然认为这个功能是一个完全有效的要求。我认为如果你在主活动中为你想要链接的东西创建一个深层链接,并在根本不使用导航控制器的情况下导航到那个地方,这是可能的。