Activityで定義したメソッドをAdapterで呼び出す方法(interfaceを使用)

## はじめに
今回は、Androidアプリ開発(Kotlin)における、`Activity`で定義したメソッドを`Adapter`から呼び出す方法に関して書いていこうと思います。
具体的な実装としては、`ListAdapter`の`setOnClickListener`メソッドが走るタイミングで、`Activity`で定義したメソッド(Fragmentを表示する)を呼び出すというものになります。

少々前置きが長いため、実装方法について見たい方は[こちらをタップ!](https://qiita.com/tomu28/items/095ba9b8b4be221d03aa#%E5%AE%9F%E8%A3%85%E3%81%AE%E6%B5%81%E3%82%8C)


## 背景
`Activity`で定義したメソッドを`Adapter`で呼び出したい時、[interface](https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/interfaces.html)を使う以外のアプローチとして、

1. `Coroutine`を使って行う方法
2. `LiveData`を使って行う方法  
3. グローバル変数をメソッド呼び出しのキッカケとする方法

などがあると考えましたが、以下の理由から、今回は`interface`を使用して実装しました。

### `Coroutine`を使ってメソッドを呼び出すアプローチを取らなかった理由

`Coroutine`のGlobalScopeは`Activity`のライフサイクルに依存せず動くため、
親となる`Activity`に`Coroutine`のGlobalScopeを付与し、`Adapter`下の`Coroutine`に親のScopeを持たせ、メソッドを走らすタイミングをGlobalScopeで管理しようと考えました。

しかし、`Adapter`を`abstract class`に変更し親のScopeを継承すると、`Activity`でUI(Fragment)を表示するメソッドが呼び出せなくなってしまうため、この方法を取りませんでした。

### `LiveData`を使って行う方法を取らなかった理由

次に、[LiveData](https://developer.android.com/topic/libraries/architecture/livedata?hl=ja)を使って値の購読処理を行い、値の変更を検知した際に`Activity`で定義したメソッドを呼び出す方法を検討しました。
具体的な流れは、`Activity`で値の購読処理を記述し、`Adapter`でボタンが押されたタイミングで購読している値を変更する。値が変更されたら、`Activity`で定義したメソッドを呼び出すというものです。

通常、LiveDataオブジェクトは ViewModelオブジェクトに格納するのが一般的ですが、今回は

- ViewModelオブジェクトを返さず、`Adapter`を使用していた
- 購読したい値は無かった

そのため、購読処理をメソッド呼び出しのためだけに利用するのは適切ではないと判断し、このアプローチを取りませんでした。

しかし、ViewModelオブジェクトを使用して`Adapter`を実装しており、

- `Adapter`内のデータ変更を検知して、Activityからメソッドを呼び出したい場合
- UIをデータの状態と一致させたい場合

は導入も手軽ですので、良い実装方法だと考えています。

### グローバル変数をメソッド呼び出しのキッカケとするアプローチを取らなかった理由

最も単純な実装方法ですが、常にグローバル変数としてメモリを使用するため、このアプローチは取りませんでした。

## 実装の流れ

前置きが長くなってしまいましたが、実装について書いていきます。

### Step1：interfaceを作成する

example)

```kotlin

interface FragmentCallInterface {
    fun setFragment()
}
```

### Step2：Activityクラスにinterfaceを継承し、定義したいメソッドを記述する

example)

```kotlin:Activity.kt
class ShoppingListActivity : AppCompatActivity(), FragmentCallInterface{

    override fun setFragment() {
        // 行いたい処理
        val fragment = ShoppingSiteFragment()
        val fragmentManager = this.supportFragmentManager
        val fragmentTransaction = fragmentManager.beginTransaction()
        fragmentTransaction.replace(R.id.container, fragment)
            .addToBackStack(null)
            .commit()
    }
    
    // ...
}

```

### Step3：interface呼び出しに関する部分を追記

まず、Adapterクラスにinterfaceに関する処理を追記します。
example)

```kotlin:Adapter.kt

class ShoppingListAdapter(private var activity: Activity, private var items: ArrayList<ShoppingItem>) : BaseAdapter() {

    // フィールドでコールバック先を宣言
    var listener: FragmentCallInterface? = null

    // ...
}
```

次は、ActivityクラスのAdapterを呼び出している部分に追記します。
example)

```kotlin:Activity.kt

// ...

var adapter = ShoppingListAdapter(this,generateData())
// 以下を追加
adapter.listener = this

// ...
```

### Step4：Adapterクラスの任意の箇所でメソッドを呼び出す

example)

```kotlin:Adapter.kt

listener?.FragmentCallInterface()
```

## おわりに

他にもこんな方法があるよ！ 等ございましたらコメントくださると嬉しいです😄
最後まで読んでいただきありがとうございました!

## 【おまけ】Java, Kotlinコード比較
### java

```Java:MyAdapter.java

import androidx.appcompat.app.AppCompatActivity;

public interface CallInterface {
    void foo();
}

public class MyAdapter extends BaseAdater{
    private MyInterface listener;

    public ShoppingListAdapter(MyInterface listener){
        this.listener = listener;
    }

    // ...
}
```

### Kotlin

```kotlin:MyAdapter.kt

import androidx.appcompat.app.AppCompatActivity

interface MyInterface {
    fun foo()
}

class MyAdapter() : BaseAdater() {
    val listener: MyInterface? = null

    // ...
}

```

## 参考
- [Kotlin リファレンス：インタフェース](https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/interfaces.html)
- [Kotlin リファレンス：LiveData](https://developer.android.com/topic/libraries/architecture/livedata?hl=ja)
- [Call Activity method from adapter](https://stackoverflow.com/questions/12142255/call-activity-method-from-adapter)