web-dev-qa-db-ja.com

異なるアクティビティにあるフラグメント間でViewModelを共有する

SharedViewModelという名前のViewModelがあります。

public class SharedViewModel<T> extends ViewModel {

    private final MutableLiveData<T> selected = new MutableLiveData<>();


    public void select(T item) {
        selected.setValue(item);
    }

    public LiveData<T> getSelected() {
        return selected;
    }
}

GoogleのArch ViewModelリファレンスページのSharedViewModelの例に基づいて実装します。

https://developer.Android.com/topic/libraries/architecture/viewmodel.html#sharing_data_between_fragments

アクティビティ内の2つ以上のフラグメントが互いに通信する必要があることは非常に一般的です。両方のフラグメントが何らかのインターフェース記述を定義する必要があり、所有者アクティビティが2つを結合する必要があるため、これは決して簡単ではありません。さらに、両方のフラグメントは、他のフラグメントがまだ作成されていないか、表示されていない場合を処理する必要があります。

ListFragmentDetailFragmentという2つのフラグメントがあります。

今までは、この2つのフラグメントをMasterActivityという名前の内部で使用していました。そして、すべてがうまくいきました。

ListFragmentでViewModelを取得し、DetailFragmentで使用する値を選択しました。

mStepSelectorViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

ただし、特定のケースでは、ListFragment(異なるデバイス構成へのレイアウト)をDetailActivityと呼ばれる異なるアクティビティに追加する必要があります。上記の例と同様にそれを行う方法はありますか?

20
alexpfx

少し遅れますが、共有ViewModelStoreを使用してこれを達成できます。フラグメントとアクティビティは、ViewModelStoreOwnerインターフェイスを実装します。これらの場合、フラグメントはインスタンスごとにストアを持ち、アクティビティは静的メンバーに保存します(構成の変更に耐えられるようにするためです)。

共有されたViewModelStoreに戻って、たとえば、アプリケーションインスタンスにしたい場合を考えてみましょう。 ViewModelStoreOwnerを実装するにはアプリケーションが必要です。

class MyApp: Application(), ViewModelStoreOwner {
    private val appViewModelStore: ViewModelStore by lazy {
        ViewModelStore()
    }

    override fun getViewModelStore(): ViewModelStore {
        return appViewModelStore
    }
}

次に、アクティビティ境界間でViewModelを共有する必要があることがわかっている場合は、次のようにします。

val viewModel = ViewModelProvider(myApp, viewModelFactory).get(CustomViewModel::class.Java)

これで、アプリで定義されたストアが使用されるようになります。そうすれば、ViewModelを共有できます。

とても重要です。この例では、アプリケーションインスタンスにViewModelsが存在するため、それらを使用するフラグメント/アクティビティが破棄されても破棄されません。したがって、それらを使用する最後のフラグメント/アクティビティのライフサイクルにリンクするか、手動で破棄する必要があります。

13
mikehc

さて、この目的のために Vita という名前のライブラリを作成しました。アクティビティ間、および異なるホストアクティビティのフラグメント間でViewModelsを共有できます。

val myViewModel = vita.with(VitaOwner.Multiple(this)).getViewModel<MyViewModel>()

このようにして作成されたViewModelは、最後のLifeCycleOwnerが破壊されるまで生き続けます。

また、アプリケーションスコープでViewModelsを作成できます。

val myViewModel = vita.with(VitaOwner.None).getViewModel<MyViewModel>()

そして、このタイプのViewModelは、ユーザーがアプリを閉じるとクリアされます

試してみて、フィードバックをお知らせください: https://github.com/FarshadTahmasbi/Vita

2

ファクトリを使用してビューモデルを作成することができ、このファクターはビューモデルの単一オブジェクトを返します。

class ViewModelFactory() : ViewModelProvider.Factory {

override fun create(modelClass: Class): T {
    if (modelClass.isAssignableFrom(UserProfileViewModel::class.Java)) {
    val key = "UserProfileViewModel"
    if(hashMapViewModel.containsKey(key)){
        return getViewModel(key) as T
    } else {
        addViewModel(key, UserProfileViewModel())
        return getViewModel(key) as T
    }
    }
    throw IllegalArgumentException("Unknown ViewModel class")
}

companion object {
    val hashMapViewModel = HashMap<String, ViewModel>()
    fun addViewModel(key: String, viewModel: ViewModel){
        hashMapViewModel.put(key, viewModel)
    }
    fun getViewModel(key: String): ViewModel? {
        return hashMapViewModel[key]
    }
}
}

活動中:

viewModelFactory = Injection.provideViewModelFactory(this)

// Initialize Product View Model
userViewModel = ViewModelProviders.of(this, viewModelFactory).get(
UserProfileViewModel::class.Java)`

これにより、アクティビティ間で共有できるUserProfileViewModelの単一オブジェクトのみが提供されます。

2
Munish Thakur

(一部ではなく)すべてのアクティビティで共有されるViewModelが必要な場合は、そのViewModelに保存したいものをApplicationクラス内に保存してください。

前回のGoogle I/Oで示された傾向は、アクティビティの概念を放棄して、多くのフラグメントを持つシングルアクティビティアプリを支持するように思われます。 ViewModelは、以前はインターフェイスのアクティビティが実装する必要があった多数のインターフェイスを削除する方法です。したがって、このアプローチはもはや巨大で維持できない活動にはなりません。

2
Marcus Wolschon

まだAndroidのMVVMフレームワークと混乱していると思います。別のアクティビティについては、必ずしも同じである必要があるため、混乱しないでください。なぜですか?

これは、同じロジックを持っている場合(他の有用なクラスでまだロジックが抽象的である場合でも)、またはXMLのビューがほとんど同一である場合に意味があります。

簡単な例を見てみましょう。

VmAというViewModelとAというアクティビティを作成し、ユーザーのデータが必要です。ユーザーのvmAにリポジトリを挿入します。

ここで、ユーザーデータを読み取る必要がある別のアクティビティが必要です。vmBという別のViewModelを作成し、その中でユーザーリポジトリを呼び出します。説明したように、リポジトリは常に同じです。

既に提案されている別の方法は、ファクトリの実装で同じViewModelのNインスタンスを作成することです。

1
AlexPad