365連休

にわかのandroidとかの開発メモ。

Android Studio 3.5 NavigationUIを使用してFragmentをリロードする

タイトルは短すぎて意味が正確ではないかも。

「NavigationUIを使用している場合に、メニューから遷移可能な宛先画面(Destination)をリロードする。」 もし、NavigationUIを使っていないなら、NavController#navigateで遷移すればいいです。

neet-rookie.hatenablog.com

 

経緯

Android Studio3.5のナビゲーションドロワーアクティビティをベースにActivityを作っているときに、ユーザ操作によって設定を書き換え、それを反映するために画面をリロードしたかった。

だが、NavController#navigateで遷移すると画面(Fragment)のonSaveInstanceStateでViewModelがヌルポになる。ViewModelインスタンスがfinalizeされた?

  1. メニュー構成は「ホーム」、「子画面」
  2. 「子画面」が表示されていたらリロード処理(NavController#navigate)する
  3. リロード後に画面を2回転させるとonSaveInstanceStateでViewModelメンバ変数がnullで死亡

 

解決

NavControllerをいじってダメだったため、ユーザ操作をエミュレートしてやれば動くと想像 ⇒ \(^o^)/

//MainActivity内に以下のメソッドを作成する。

    /** ナビゲーションによって表示されている画面をリロードする */
    public void reloadDestination(){
        final @NonNull NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); //コントローラ
        final @Nullable NavDestination currentDestination = navController.getCurrentDestination(); //現在の画面
        final @NonNull NavigationView navigationView = findViewById(R.id.nav_view); //メニュー
        if(currentDestination!=null) {
            final @Nullable MenuItem item = navigationView.getMenu().findItem(currentDestination.getId()); //MenuItem逆引き
            if (item != null) { //逆引き成功
                NavigationUI.onNavDestinationSelected(item, navController);
                //navController.navigate(resId); 直接navigateを呼び出すとバグる
            }
        }
    }

    /** ナビゲーションによって表示されている画面をリロードする
     * @param matchIDs リロード対象の画面、一致しなければ何もしない
     */
    public void reloadDestination(int... matchIDs){
        final @NonNull NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); //コントローラ
        final @Nullable NavDestination currentDestination = navController.getCurrentDestination(); //現在の画面
        final @NonNull NavigationView navigationView = findViewById(R.id.nav_view); //メニュー
        if(currentDestination!=null) {
            for (int resId : matchIDs) {
                if (currentDestination.getId() == resId) { //現在の画面と引数が一致
                    final @Nullable MenuItem item = navigationView.getMenu().findItem(resId); //MenuItem逆引き
                    if (item != null) { //逆引き成功
                        NavigationUI.onNavDestinationSelected(item, navController);
                        //navController.navigate(resId); 直接navigateを呼び出すとバグる
                    }
                }
            }
        }
    }