【kotlin】超簡単なMVPを実装してみた、④異なるレイアウト(Fragment)との連携

# はじめに
今回はMVP(アーキテクチャ)に沿って、異なるレイアウト(Fragment)との連携を実装していきたいと思います。

# 実装内容
・Activity(親View)の下半分にFragment(子View)を重ねる。
・ACTIVITYボタン(紫文字)が押された際に双方ViewのTextViewに1を足し、表示する。
・FRAGEMENTボタン(赤文字)が押された際に双方ViewのTextViewに10を足し、表示する。
![Screenshot_1642842015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784089/de1e3daa-05f7-395f-8ffc-b3be880af641.png)

# 実装
**[レイアウト]**
Activity(親)側
①activityButton
②activityTextView
Fragment(子)側
③fragmentButton
④fragmentTextView
![Screenshot_1642842015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/784089/68f00918-30c3-44a1-8ce6-02d63523085a.png)

**[コード]**
**・準備**
①BasePresenter,BaseViewを作成。

```kotlin:BasePresenter
interface BasePresenter {
　    //起動処理のメソッド
    fun start()
}
```

```kotlin:BaseView
interface BaseView<T> {
    var presenter: T
}
```

**・Contract設定**
①Contractを作成。
②Activity、Fragment、Presenterのインターフェースを作成
③ActivityとFragmentとpresenterで使用するメソッドを作成。

```kotlin:MainContract
interface MainContract {

    interface Activity: BaseView<Presenter> {
        //③Activity側のTextViewをテキストを表示するメソッド
        fun showActivityTextView (text: String)
    }

    interface Fragment: BaseView<Presenter> {
        //③Fragment側のTextViewをテキストを表示するメソッド
        fun showFragmentTextView (text: String)
    }

    interface Presenter: BasePresenter {
        //③Activity側のButoonをクリックされた際に使用するメソッド
        fun onClickActivityButton ()
        //③Fragment側のButoonをクリックされた際に使用するメソッド
        fun onClickFragmentButton ()
    }
}
```

**・Presenter設定**
①Presenter作成。
②Contract.Activityを引数に追加。
③Contract.Fragmentを引数に追加。
④Contract.presenter(MainContract.Presenter)を戻り値に追加。
⑤activity.presenter = thisに設定。
⑥fragment.presenter = thisに設定。
⑦Contractで作成したメソッドが色々出てくるので設定。

```kotlin:MainPresenter
class MainPresenter(private val activity: MainContract.Activity,
                    private val fragment: MainContract.Fragment): MainContract.Presenter {

    //数字をカウントする変数
    var cnt = 0

    //初期化
    init {
        //⑤activity.presenter = thisに設定。
        activity.presenter = this
        //⑥fragment.presenter = thisに設定。
        fragment.presenter = this
    }

    //⑦Activity側のボタンが押された時の処理
    override fun onClickActivityButton() {
        cnt += 1
        activity.showActivityTextView(cnt.toString())
        fragment.showFragmentTextView(cnt.toString())
    }
    //⑦Fragment側のボタンが押された時の処理
    override fun onClickFragmentButton() {
        cnt += 10
        activity.showActivityTextView(cnt.toString())
        fragment.showFragmentTextView(cnt.toString())
    }

    override fun start() {
    }
}
```

**・Fragment(子View)設定**
①Fragment作成。
②Fragmentの戻り値にContract.Fragmentを追加。
③fragmentを親Viewに渡す。
④overrideしたContract.presenter(MainContract.Presenter)を追加。
⑤Contractで作成したメソッドが色々出てくるので設定。
⑥presenterのメソッドを呼び出す

```kotlin:MainFragment
//②Fragmentの戻り値にContract.Fragmentを追加。
class MainFragment : Fragment(), MainContract.Fragment {

    ////④overrideしたContract.presenter(MainContract.Presenter)を追加。
    override lateinit var presenter: MainContract.Presenter

    lateinit var fragmentTextView :TextView
    lateinit var fragmentButton :Button

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        fragmentTextView = view.findViewById(R.id.fragmentTextView)
        fragmentButton = view.findViewById(R.id.fragmentButton)
        //③fragmentを親Viewに渡す。
        activity?.let {
            if (it is MainActivity) {
                it.initPresenter(this)
            }
        }


        fragmentButton.setOnClickListener {
            //⑥presenter.onClickFragmentButtonを呼び出す。
            presenter.onClickFragmentButton()
        }
    }

    //⑤FragmentTextViewに文字を表示させる
    override fun showFragmentTextView(text: String) {
        fragmentTextView.text = text
    }
}

```

**・Activity(親View)設定**
①Activityの戻り値にContract.Activityを追加。
②overrideしたContract.presenter(MainContract.Presenter)を追加。
③Contractで作成したメソッドが色々出てくるので設定。
④presenterのメソッドを呼び出す。

```kotlin:MainActivity
//①戻り値にContract.Activityを追加。
class MainActivity : AppCompatActivity(), MainContract.Activity {

    //②overrideしたContract.presenter(MainContract.Presenter)を追加。
    override lateinit var presenter: MainContract.Presenter

    //空の子Viewを用意する。
    var mainFragment : MainFragment? = null

    lateinit var activityTextView : TextView
    lateinit var activityButton : Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //③Fragment設定(引数に自分を入れる)。
        val transition = supportFragmentManager.beginTransaction()
        transition.add(R.id.fragmentLayout, MainFragment())
        transition.commit()


        activityTextView = findViewById(R.id.activityTextView)
        activityButton = findViewById(R.id.activityButton)

        activityButton.setOnClickListener {
            //⑤presenter.onClickActivityButtonを呼び出す。
            presenter.onClickActivityButton()
        }
    }
    //③ActivityTextViewに文字を表示させる
    override fun showActivityTextView(text: String) {
        activityTextView.text = text
    }

    fun initPresenter(main : MainFragment? = null) {
        if (::presenter.isInitialized) {
            return
        }
        main?.let {
            this.mainFragment = it
        }
        //子Viewがnulじゃない場合、Presenterを起動する。
        if (mainFragment != null) {
            presenter = MainPresenter(this, mainFragment!!)
        }
    }
}
```

以上、異なるレイアウト(Fragment)との連携でした。
ロジカルな処理がpresenterに集まって、いい感じになったと思います。

Viewが一つだけ相違点をまとめると以下となっております。
・Viewの数だけContractでインターフェースを作成する。
・親Viewに子Viewを渡し、Presenterの初期化を行う。

# 関連記事
[【kotlin】超簡単なMVPを実装してみた、①Presenterの実装](https://qiita.com/Na0yaKub0/items/d688c440dfa9c3a49a7f)
[【kotlin】超簡単なMVPを実装してみた、②LocalRepository設定(ローカルデータに保存、読み込み)](https://qiita.com/Na0yaKub0/items/7209258a0d7b084d276a)
[【kotlin】超簡単なMVPを実装してみた、③RemoteRepository設定(APIのやり取り)](https://qiita.com/Na0yaKub0/items/57358d689b6552ad7d75)
[【kotlin】超簡単なMVPを実装してみた、⑤recyclerviewとの連携](https://qiita.com/Na0yaKub0/items/fc47abf1e6e97f4b8c8b)

# 参考資料
[Android アプリ設計パターン入門](https://peaks.cc/books/architecture_patterns)
[テストが書けない人のAndroid MVP](https://www.amazon.co.jp/%E3%83%86%E3%82%B9%E3%83%88%E3%81%8C%E6%9B%B8%E3%81%91%E3%81%AA%E3%81%84%E4%BA%BA%E3%81%AEAndroid-MVP-%E6%8A%80%E8%A1%93%E3%81%AE%E6%B3%89%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%EF%BC%88NextPublishing%EF%BC%89-%E9%AB%98%E7%95%91-%E5%8C%A1%E7%A7%80/dp/4844398717)
[todo-mvp](https://github.com/android/architecture-samples/tree/todo-mvp-kotlin/todoapp)