web-dev-qa-db-ja.com

ナビゲーションコンポーネントを使用して、ビューモデルを複数のフラグメント(アクティビティではない)にスコープする

私はナビゲーションコンポーネントを使用しています。ビューモデルをいくつかのフラグメント間で共有したいのですが、フラグメントを残したときにそれらをクリアする必要があります(したがって、それらをアクティビティにスコープしていません)1つのアクティビティに多数のフラグメントを取得しようとしていますアプローチ。複数のナビゲーションホストを使用し、getParentFragmentを使用してフラグメントをスコープすることでこれを達成できましたが、これにより、他の親フラグメントでフラグメントをラップしなければならない問題が多くなり、戻るボタンがシームレスに機能し、他のハックが機能して何かが機能するはずですかなりシンプルに。これを達成する方法について誰かが良い考えを持っていますか?使用できるgetViewModelStoreに何かがあるかどうか疑問に思いました、以下の画像を考えて、ビューモデルをcreateCardFragment2にスコープ指定し、それ以降の何か(addPredictions、editImageFragmentなど、まだ追加していないもの)で使用したいのですが、 mainFragmentに戻り、ビューモデルをクリアします。

ところで、ここにはクリアしてはならない他のビューモデルがあるため、mainFragmentビューモデルストアで単にclearを呼び出すことはできません。私が知っている親フラグメントが何であるかをnavホストに通知する方法が欲しいと思います事になる、またはmainFragmentまたはcardPreviewFragmentから移動している場合にビューモデルを新しくする方法

nav graph

10
martinseal1987

はい、ビューモデルをandroidx.navigation:*:2.1.0-alpha02で始まるnavgraphにスコープすることが可能です。リリースノート ここ およびAPIの例 ここ を参照してください。あなたが与える必要があるすべてはあなたのnavgraphのためのR.idです。ただし、通常はビューモデルがonCreateで初期化されるため、使用するのが少し面倒です。これは、navコントローラーがnavホストフラグメントによってまだ設定されていないため、このスコープでは不可能です(私はこれが構成変更の場合に当てはまることを発見しています。

また、mainFragmentをそのスコープの一部にしたくない場合は、それを削除して、おそらく nested nav graph を使用することをお勧めします。

6
Alex H

アレックスHの承認された回答の具体的な例を次に示します。

Build.gradle(アプリ)

dependencies {
    def nav_version = "2.1.0"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
}

ビューモデルの例

class MyViewModel : ViewModel() { 
    val name: MutableLiveData<String> = MutableLiveData()
}

あなたのFirstFlowFragment.ktで定義します

val myViewModel: MyViewModel by navGraphViewModels(R.id.your_nested_nav_id)
myViewModel.name.value = "Cool Name"

そしてSecondFlowFragment.ktで定義します

val myViewModel: MyViewModel by navGraphViewModels(R.id.your_nested_nav_id)
val name = myViewModel.name.value.orEmpty()
Log.d("tag", "welcome $name!")

ViewModelはこのネストされたフラグメントでスコープされ、ネストされたnavが破棄されると共有状態も破棄されるため、手動でリセットする必要はありません。

3
Miko Chu

これを投稿したとき、機能はありましたが期待どおりに機能しませんでした。それ以来、これを常に使用していて、この質問はますます注目を集めているので、最新の例を投稿すると思います、

使用する

//Navigation
implementation "androidx.navigation:navigation-fragment:2.2.0-rc04"
// Navigation UI
implementation "androidx.navigation:navigation-ui:2.2.0-rc04"

私はこのようにビューモデルの店主を取得します

private ViewModelStoreOwner getStoreOwner() {

        NavController navController = Navigation
                .findNavController(requireActivity(), R.id.root_navigator_fragment);
        return navController.getViewModelStoreOwner(R.id.root_navigator);
}

私は1つのアクティビティの複数のフラグメントの実装を使用していますが、これを使用すると、ビューモデルをスコープされたフラグメントのみに効果的に結び付けることができ、新しい live data を使用して制限することもできます

最初のIDはナビゲーショングラフフラグメントから取得されます

<?xml version="1.0" encoding="utf-8"?>
  <FrameLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    xmlns:app="http://schemas.Android.com/apk/res-auto">

    <fragment
      Android:id="@+id/root_navigator_fragment"
      Android:layout_width="match_parent"
      Android:layout_height="match_parent"
      Android:name="androidx.navigation.fragment.NavHostFragment"
      app:defaultNavHost="true"
      app:navGraph="@navigation/root_navigator"/>

  </FrameLayout>

2番目はナビゲーショングラフのIDから取得されます

  <?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/root_navigator"
    app:startDestination="@id/mainNavFragment">

そして、あなたはそれをそのように使うことができます

private void setUpSearchViewModel() {
    searchViewModel = new ViewModelProvider(getStoreOwner()).get(SearchViewModel.class);
}
1
martinseal1987