LoginSignup
2
1

More than 5 years have passed since last update.

AnkoとData Bindingを同時に使えないので委譲プロパティを使ってなんとかする

Posted at

AnkoとData Bindingを同時に使えない

AnkoとData Bindingは同時に使うことができません。
DataBindingではXMLの名称から ~~Bindingという名称のクラス(仮にBindingクラスと呼ぶことにします)を自動的に生成してくれますが、Ankoでレイアウトを作成した場合にはBindingクラスを生成してくれません。

Data Bindingのように、Modelを変更したときにViewを更新してくれるようにしたいときにはどうすれば良いのでしょうか?
Data Bindingの完全な代替にはなりませんが、委譲プロパティを使用すればこのようなときに期待の動作をさせることができます。

委譲プロパティ

委譲プロパティ(Delegated Properties)を利用すると、あるプロパティにアクセスする際にログを出したりなどといった特定の処理を挟むことができます。

プロパティの後ろに by ~ と書くことで 特定のオブジェクトに処理を委譲できます。
委譲先のクラスとして下記のようなクラスを作成します。

class Counter() : ReadWriteProperty<Any, Int> {
    private var mValue: Int? = null

    override fun getValue(thisRef: Any, property: KProperty<*>): Int {
        return mValue as Int
    }

    override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {
        Log.d("mydebug", "${property.name}: $value")
        mValue = value
    }
}

下記のようにby Counter()とすればcountへの代入時にCounterクラスのsetValue関数が呼ばれます。Counter()は単なるCounterクラスのコンストラクタです。

var count: Int by Counter()

// 省略

count = 1
count++
count = 10
count: 1
count: 2
count: 10

同じように、プロパティの値を取得するときにはgetValue関数が呼び出されます。

下のようにCounterクラスのコンストラクタに値や関数を渡すこともできます。

class Counter<T>(val function: (property: KProperty<*>, T) -> Unit) : ReadWriteProperty<Any, T> {
    private var mValue: T? = null

    override fun getValue(thisRef: Any, property: KProperty<*>): T {
        return mValue as T
    }

    override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
        mValue = value
        function(property, value)
    }
}
var count: Int by Counter { property, value ->
    Log.d("mydebug", "${property.name}: $value")
}

なんとかする

以下のように委譲プロパティを使えば、Modelを変更したときにViewを更新する一方向バインディングができます。

ViewBinder.kt
class ViewBinder<M>(val function: (M) -> Unit) : ReadWriteProperty<Any, M> {
    private var mValue: M? = null

    override fun getValue(thisRef: Any, property: KProperty<*>): M {
        return mValue as M
    }

    override fun setValue(thisRef: Any, property: KProperty<*>, value: M) {
        if (mValue == value) {
            return
        }
        mValue = value
        function(value)
    }
}
MyActivityUI
class MyActivityUI() :AnkoComponent<MainActivity>() {

    var user: User? by ViewBinder {
        textView.text = user?.name

        // 省略
    }

    // 省略

}

参考記事
ViewBinder for Android in Kotlin

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1