web-dev-qa-db-ja.com

単一のイベントを発行し、最後にサブスクライブしたオブザーバーのみに通知するLiveDataを作成する方法は?

この example のように単一のイベントを発生するライブデータを作成しました。

私の質問は次のとおりです:LiveDataの値が変更されたときに最後にサブスクライブしたオブザーバーのみに通知する方法は?

頭に浮かぶのは、SingleLiveDataクラスのリンクリストにオブザーバーを格納し、渡されたオブザーバーがリストの最後の要素と同じ場合にのみsuper.observeを呼び出すことです。

これが最善の方法かどうかはわかりません。

このメカニズムを使用して、FABクリックイベントを、アクティビティからViewPager内に表示されるフラグメントに伝達します。フラグメントはビューページャーアダプターに動的に追加されるため、フラグメントの順序がわかっているとしましょう。

5
TheTechWolf

最後に、この問題の回避策を見つけました。単一のイベントを発生させるライブデータから離れる必要がありました。これは、必要な動作をすることができなかったためです。

これの代わりに、JoseAlcérrecaによるこの article の最後の段落のように、データをラップするイベントオブジェクトを生成する単純な可変ライブデータを使用しました。

ビューページャーにフラグメントを表示しているので、一度に表示できるフラグメントは1つだけです。

だから私のビューモデルは次のようになります:

class ActionViewModel : ViewModel() {
  private val onCreateLiveData: MutableLiveData<Event<String>> = MutableLiveData()

  fun observeOnCreateEvent(): LiveData<Event<String>> = onCreateLiveData

  fun onCreateCollectionClick(message: String) {
    this.onCreateLiveData.value = Event(message)
  }
}

イベントラッパークラスの実装は次のようになります。

/*Used as a wrapper for data that is exposed via a LiveData that represents an 
 event.*/

open class Event<out T>(private val content: T) {

  var hasBeenHandled = false
    private set // Allow external read but not write

  /**
   * Returns the content and prevents its use again.
  */
  fun getContentIfNotHandled(): T? {
    return if (hasBeenHandled) {
      null
    } else {
      hasBeenHandled = true
      content
    }
  }

  /**
    * Returns the content, even if it's already been handled.
  */
  fun peekContent(): T = content
}

フラグメントで、次のようなイベントを観察できます。

override fun onActivityCreated(savedInstanceState: Bundle?) {
   super.onActivityCreated(savedInstanceState)

   actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionViewModel::class.Java)
   actionViewModel.observeOnCreateEvent()
       .observe(this, Observer {
         it?.takeIf { userVisibleHint }?.getContentIfNotHandled()?.let {
           //DO what ever is needed
         }
       })
}

FragmentuserVisibleHintプロパティは、フラグメントが現在ユーザーに表示されている場合にtrueを返します。一度に表示されるフラグメントは1つだけなので、これは私たちにとってはうまくいきます。これは、フラグメントが表示されている場合にのみ、eventデータにアクセスすることを意味します。

また、Eventラッパーの実装では値の読み取りが1回しか許可されないため、Observerがこのイベントを取得するたびに、その値はnullになり、無視されます。

結論:この方法では、最後にサブスクライブしたオブザーバーのみに通知する単一イベントのライブデータをシミュレートしています。

0
TheTechWolf

私はソリューションを作成しました。自由に見てください https://github.com/ueen/LiveEvent

0
ueen