はじめに
今回は、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)
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)
class ShoppingListAdapter(private var activity: Activity, private var items: ArrayList<ShoppingItem>) : BaseAdapter() {
// フィールドでコールバック先を宣言
var listener: FragmentCallInterface? = null
// ...
}
次は、ActivityクラスのAdapterを呼び出している部分に追記します。
example)
// ...
var adapter = ShoppingListAdapter(this,generateData())
// 以下を追加
adapter.listener = this
// ...
Step4:Adapterクラスの任意の箇所でメソッドを呼び出す
example)
listener?.FragmentCallInterface()
おわりに
他にもこんな方法があるよ! 等ございましたらコメントくださると嬉しいです😄
最後まで読んでいただきありがとうございました!
【おまけ】Java, Kotlinコード比較
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
import androidx.appcompat.app.AppCompatActivity
interface MyInterface {
fun foo()
}
class MyAdapter() : BaseAdater() {
val listener: MyInterface? = null
// ...
}