KotlinでActivityのメソッドをAdapterから呼ぶ方法
KotlinのActivityで定義したメソッドをAdapterから呼び出すには?Interfaceを活用した最適な実装方法を、コード例と共に解説!
はじめに
今回は、Androidアプリ開発(Kotlin)における、Activityで定義したメソッドをAdapterから呼び出す方法に関して書いていこうと思います。具体的な実装としては、ListAdapterのsetOnClickListenerメソッドが走るタイミングで、Activityで定義したメソッド(Fragmentを表示する)を呼び出すというものになります。
少々前置きが長いため、実装方法について見たい方はこちらをタップ!
背景
Activityで定義したメソッドをAdapterで呼び出したい時、interfaceを使う以外のアプローチとして、
Coroutineを使って行う方法LiveDataを使って行う方法- グローバル変数をメソッド呼び出しのキッカケとする方法
などがあると考えましたが、以下の理由から、今回はinterfaceを使用して実装しました。
Coroutineを使ってメソッドを呼び出すアプローチを取らなかった理由
CoroutineのGlobalScopeはActivityのライフサイクルに依存せず動くため、親となるActivityにCoroutineのGlobalScopeを付与し、Adapter下のCoroutineに親のScopeを持たせ、メソッドを走らすタイミングをGlobalScopeで管理しようと考えました。
しかし、Adapterをabstract classに変更し親のScopeを継承すると、ActivityでUI(Fragment)を表示するメソッドが呼び出せなくなってしまうため、この方法を取りませんでした。
LiveDataを使って行う方法を取らなかった理由
次に、LiveDataを使って値の購読処理を行い、値の変更を検知した際にActivityで定義したメソッドを呼び出す方法を検討しました。具体的な流れは、Activityで値の購読処理を記述し、Adapterでボタンが押されたタイミングで購読している値を変更する。値が変更されたら、Activityで定義したメソッドを呼び出すというものです。
通常、LiveDataオブジェクトは ViewModelオブジェクトに格納するのが一般的ですが、今回は
- ViewModelオブジェクトを返さず、
Adapterを使用していた - 購読したい値は無かった
そのため、購読処理をメソッド呼び出しのためだけに利用するのは適切ではないと判断し、このアプローチを取りませんでした。
しかし、ViewModelオブジェクトを使用してAdapterを実装しており、
Adapter内のデータ変更を検知して、Activityからメソッドを呼び出したい場合- UIをデータの状態と一致させたい場合
は導入も手軽ですので、良い実装方法だと考えています。
グローバル変数をメソッド呼び出しのキッカケとするアプローチを取らなかった理由
最も単純な実装方法ですが、常にグローバル変数としてメモリを使用するため、このアプローチは取りませんでした。
実装の流れ
前置きが長くなってしまいましたが、実装について書いていきます。
Step1:interfaceを作成する
example:
interface FragmentCallInterface {
fun setFragment()
}Step2:Activityクラスにinterfaceを継承し、定義したいメソッドを記述する
example:
- Activity.kt
class ShoppingListActivity : AppCompatActivity(), FragmentCallInterface{
override fun setFragment() {
// 行いたい処理
val fragment = ShoppingSiteFragment()
val fragmentManager = this.supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.page-frame, fragment)
.addToBackStack(null)
.commit()
}
// ...
}Step3:interface呼び出しに関する部分を追記
まず、Adapterクラスにinterfaceに関する処理を追記します。
example:
- Adapter.kt
class ShoppingListAdapter(private var activity: Activity, private var items: ArrayList<ShoppingItem>) : BaseAdapter() {
// フィールドでコールバック先を宣言
var listener: FragmentCallInterface? = null
// ...
}次は、ActivityクラスのAdapterを呼び出している部分に追記します。
example:
- Activity.kt
// ...
var adapter = ShoppingListAdapter(this,generateData())
// 以下を追加
adapter.listener = this
// ...Step4:Adapterクラスの任意の箇所でメソッドを呼び出す
example:
- Adapter.kt
listener?.FragmentCallInterface()おわりに
最後まで読んでいただきありがとうございました😄
おまけ:Java, Kotlinコード比較
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
- MyAdapter.kt
import androidx.appcompat.app.AppCompatActivity
interface MyInterface {
fun foo()
}
class MyAdapter() : BaseAdater() {
val listener: MyInterface? = null
// ...
}