[Scala, Android] Publisher, Subscriberでイベント通知を実装してみる

### 入れ子のFragment内のイベントを上位に伝搬するのってめんどう、もっとダイレクトな感じにしたい

Fragment内で発生したイベントを上位のActivityやFragmentに通知する場合、 
FragmentのListenerを作ったり、delegaterでイベントを伝搬させたりと、入れ子構造が深くなるほど実装が面倒で追いにくいなと思っていました。


### scala.collection.mutable.Publisher Subscriber

Document

- http://www.scala-lang.org/api/current/index.html#scala.collection.mutable.Publisher
- http://www.scala-lang.org/api/current/index.html#scala.collection.mutable.Subscriber

Publisher[A, This] は、A型のイベントを すべての登録済みsubscriberにpublishする。
フィルタを指定することで、subscriberに送信されるイベントの数を制限したりできるようです。
一般的にmixinして使われるとのこと


あまりサンプルがなかったんですが、こちらを参考にしました。

- http://stackoverflow.com/questions/7892155/publisher-subscribe-in-scala
- http://stackoverflow.com/questions/3755453/scala-listener-observer


上記のPublisher、Subscriberを使ってViewのイベントを上位に通知してみます。
デザインパターンではObserverパターンと呼ばれる類


### 全体イメージ

SampleFragmentViewに設置したボタンをタップしたら、Activityにイベントを通知する
Activityは通知を受け取って、トーストを表示するという感じの実装をします。

<img width="250" alt="スクリーンショット 2015-12-28 13.11.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/1698/0434d30a-93fb-efbc-8192-2f0e8740969f.png">



### 実装

最上位のSampleActivity(Subscriber)

```scala

package com.sample.application

import android.os.Bundle
import android.support.v4.app.FragmentActivity
import android.util.Log
import android.widget.Toast
import com.sample.R

import scala.collection.mutable

// 通知されるイベントの型
case class MyEvent(message: String)

class SampleActivity extends FragmentActivity
with mutable.Subscriber[MyEvent, mutable.Publisher[MyEvent]] {

  var fragment: Option[SampleFragment] = None

  override def onCreate(savedInstanceState: Bundle): Unit = {
    super.onCreate(savedInstanceState)
    Log.d(getClass.getSimpleName, "##onCreate")

    setContentView(R.layout.sample_activity_layout)
    fragment = Some(new SampleFragment)

    getSupportFragmentManager.beginTransaction()
      .replace(R.id.fragment_container, fragment.get).commit()
  }

  override def onResume(): Unit = {
    super.onResume()
    Log.d(getClass.getSimpleName, "##onResume")

    // subscriberに登録
    for {
      fm <- fragment
      v <- fm.view
    } yield v.subscribe(this)
  }

  //mutable.Subscriberの実装
  override def notify(pub: mutable.Publisher[MyEvent], event: MyEvent): Unit = {
    Toast.makeText(getApplicationContext, event.message, Toast.LENGTH_LONG).show()
  }

}
```

SampleActivityに装着される SampleFragment

```scala

package com.sample.application

import android.os.Bundle
import android.support.v4.app.Fragment
import android.util.Log
import android.view.{LayoutInflater, View, ViewGroup}
import com.sample.R

class SampleFragment extends Fragment {

  var view: Option[SampleFragmentView] = None

  override def onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle): View = {
    inflater.inflate(R.layout.sample_fragment_layout, container, false)
  }

  override def onStart(): Unit = {
    super.onStart()
    Log.d(getClass.getSimpleName, "##onStart")

    view = Some(new SampleFragmentView(getActivity))
    view.get.configure()
  }

}
```

SampleFragmentView(Publicser)

```scala

package com.sample.view

import android.support.v4.app.FragmentActivity
import android.view.View
import android.view.View.OnClickListener
import com.sample.R

import scala.collection.mutable

class SampleFragmentView(activity: FragmentActivity) extends mutable.Publisher[MyEvent] {

  private val btn = activity.findViewById(R.id.sample_button)

  def getRandomStr = new scala.util.Random(new java.security.SecureRandom()).alphanumeric.take(5).mkString

  def configure(): Unit = {
    //ボタンがクリックされたら登録されているSubscriberにMyEventが発生したことを通知します。
    btn.setOnClickListener(new OnClickListener {
      override def onClick(view: View): Unit = pub("通知したよー" + getRandomStr)
    })
  }

  def pub(message: String) = publish(MyEvent(message))

}
```

### 結果
できました。回転しても大丈夫

![スクリーンショット 2015-12-28 13.59.05.png](https://qiita-image-store.s3.amazonaws.com/0/1698/4d5564f3-239e-91bc-4e8c-5ebcccf545a5.png)


### まとめ
最後まで読んでいただいてありがとうございました。
Fragmentってコンストラクタ引数から渡せなないとか、色々と面倒なことも多いですが、
今回のようにイベント通知など含め少しでもシンプルに実装できたらなと常に思っています。

今回はAndroidでしたが、 mutable.Publisherの実装例も少なかったので少しでも参考になれば幸いです。