web-dev-qa-db-ja.com

Androidナビゲーションコンポーネントポップから移行の問題

2つのアクションがあります

Action1

 <action
        Android:id="@+id/actionBaseFragmentToAskForLocation"
        app:destination="@+id/introAskForLocationFragment"
        app:enterAnim="@anim/slide_in_right"
        app:exitAnim="@anim/slide_out_left"
        app:popEnterAnim="@anim/slide_in_left"
        app:popExitAnim="@anim/slide_out_right" />

Action2

<action
        Android:id="@+id/actionIntroAskLocationToLogin"
        app:destination="@id/loginFragment"
        app:enterAnim="@anim/slide_in_right"
        app:exitAnim="@anim/slide_out_left"
        app:popEnterAnim="@anim/slide_in_right"
        app:popExitAnim="@anim/fade_out"
        app:popUpTo="@+id/app_main_navigation" />

私が欲しいのは、2番目のアクションがトリガーされたときにバックスタックをクリアし、loginFragmentのみをスタックに残るように設定することです。

ただ1つの問題は、Action2を実行すると、「slide_out_right」が終了アニメーションとして実行されることです。

スタックからフラグメントをポップすると、action2の「exitAnim」ではなく、action1の「popExitAnim」がトリガーされることを理解しています。

しかし、フラグメントを終了するためにslide_out_leftアニメーションを実行させ、スタックからポップする方法を知りたいです。

10
jaydeep_gedia

onCreateAnimationを呼び出すフラグメントのnavigateをオーバーライドしました。この例では、ネストされたnavグラフをIDでナビゲートし、pop exitアニメーション(またはpopExitAnim)を条件付きで置き換える方法を示します。

override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
    val navController = findNavController()
    val graph = navController.graph.findNode(R.id.onboardingGraph) as NavGraph
    val dest = graph.findNode(R.id.confirmationFragment)
    if (!enter && dest != null && navController.currentDestination?.id == dest.id) {
        return AnimationUtils.loadAnimation(requireContext(), R.anim.slide_out_left)
    }
    return super.onCreateAnimation(transit, enter, nextAnim)
}

この特定の状況は、スライドアニメーションの方向性に一部起因することに注意してください。

6
maxbeaudoin

これは、NavOptionsがドロワーをナビゲーショングラフにバインドする際に使用される便利なメソッドによって内部的に処理されるため、解決するのが少し難しいです。私はもともと、設定メニューとonOptionsItemSelectedでこのソリューションをテストしましたが、基本的な考え方はここでも機能するはずです。

まず、メニュー項目IDがナビゲーションフラグメントのIDに対応していることを確認します。

<menu xmlns:Android="http://schemas.Android.com/apk/res/Android">

    ...

    <item Android:id="@+id/example_id" ... />
</menu>
<navigation xmlns:Android="http://schemas.Android.com/apk/res/Android" ... >

    ...

    <fragment Android:id="@+id/example_id" ... />
</navigation>

これで、 既製のメソッドを使用してドロワーをNavControllerに接続するのではなくNavigationView.OnNavigationItemSelectedListenerあなたのNavHostアクティビティでonNavigationItemSelectedメソッドをオーバーライドします:

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    NavHost navHost = Navigation.findNavController(this, R.id.your_nav_Host_fragment);
    return NavigationUI.onNavDestinationSelected(item, navHost);
}

これにより、選択がグラフのナビゲーションとして転送されます。置換your_nav_Host_fragmentを設定したフラグメントIDでapp:defaultNavHost="true"

これは機能しますが、スライドアニメーションがデフォルトのままであることに気付くでしょう。これは、 NavigationUI呼び出しが以下の設定で内部的に独自のNavOptions を作成するためです。

NavOptions.Builder builder = new NavOptions.Builder()
                .setLaunchSingleTop(true)
                .setEnterAnim(R.anim.nav_default_enter_anim)
                .setExitAnim(R.anim.nav_default_exit_anim)
                .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
                .setPopExitAnim(R.anim.nav_default_pop_exit_anim);

残念ながら、このメソッドはまだNavOptions.Builderを引数として使用しますが、機能を模倣するAndroidソースコードに基づいてユーティリティクラスを作成できます。

public class NavigationUIHelper {
    public static boolean onNavDestinationSelected(@NonNull MenuItem item,
                                                   @NonNull NavController navController,
                                                   @NonNull NavOptions.Builder builder) {
        if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
            NavDestination destination = findStartDestination(navController.getGraph());
            builder.setPopUpTo(destination.getId(), false);
        }
        NavOptions options = builder.build();
        try {
            navController.navigate(item.getItemId(), null, options);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }

    // Need to copy this private method as well
    private static NavDestination findStartDestination(@NonNull NavGraph graph) {
        NavDestination startDestination = graph;
        while (startDestination instanceof NavGraph) {
            NavGraph parent = (NavGraph) startDestination;
            startDestination = parent.findNode(parent.getStartDestination());
        }
        return startDestination;
    }
}

最後に、アクティビティでNavigationUIへの呼び出しをNavigationUIHelperで実装された呼び出しに置き換えることができます。

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    NavHost navHost = Navigation.findNavController(this, R.id.your_nav_Host_fragment);
    NavOptions.Builder builder = new NavOptions.Builder()
                .setLaunchSingleTop(true)
                .setEnterAnim(R.anim.custom_enter)
                .setExitAnim(R.anim.custom_exit)
                .setPopEnterAnim(R.anim.custom_pop_enter)
                .setPopExitAnim(R.anim.custom_pop_exit);
    return NavigationUIHelper.onNavDestinationSelected(item, navHost, builder);
}

これにより、ナビゲーションコンポーネントを交換しなくても、好みに応じて引き出しの切り替えアニメーションを変更できます。

2
Jonatan