欲しいなぁと思っていた機能が簡単に実装可能なのでメモ
はじめに
この記事は独自に実装したViewに対して、
EditText.textへの双方向DataBindingのようなものを実装するための方法を記載します
<EditText
...
android:text="@={viewModel.text}" />
これを、こうして双方向DataBindingする方法
<jp.honkot.CustomizedEditText
...
app:text="@={viewModel.text}" />
なお、この例では初歩的なBaseObservableを利用していますが、
ObservableFieldやMutableLiveDataでも代用可能です。
前提
- DataBindingの利用方法を心得ている
- 双方向DataBindingの使い方を心得ている
実装イメージ
図にするとこんな感じ
実装
順を追って実装しましょう
Step.1 独自Viewの実装
割愛しますが、まぁこんな感じで書くと思います
class CustomEditText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
...
}
Step.2 InverseBindingMethodsアノテーションを指定
独自ViewクラスにInverseBindingMethodsアノテーションを実装します
このattribute
で指定した属性名がそのままXMLファイル上で双方向DataBindingで指定する属性になります
@InverseBindingMethods(
InverseBindingMethod(
type = CustomEditText::class, // 対象のクラス
attribute = "text" // 作成する属性
)
)
class CustomEditText @JvmOverloads constructor(
Step.3 Getter/Setterを用意
指定した双方向DataBindingでやりとりするGetter/Setterを指定
なお、ここではCustomizedEditTextの中でもViewModelが存在し、
その中の属性も双方向DataBindingをしていると仮定する
fun getText(): String {
return viewModel.input
}
fun setText(value: String) {
viewModel.input = value
}
class ViewModel : BaseObservable() {
@Bindable
var input: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.input)
}
}
Step.4 InverseBindingListenerを設置
このInverseBindingListenerが、双方向DataBindingで情報をやりとりするインターフェースだと思ってください。
これを独自Viewに持たせる必要があります。この例では独自ViewのViewModelに持たせます
fun setInverseBindingListener(listener: InverseBindingListener) {
viewModel.listener = listener
}
class ViewModel : BaseObservable() {
@Bindable
var input: String = ""
set(value) {
field = value
listener?.onChange() // ここ、重要
notifyPropertyChanged(BR.input)
}
var listener: InverseBindingListener? = null
}
Step.5 BindingAdapterを作成
XMLに記載するためのBindingAdapterを作成
このアノテーションで定義することで、指定Viewに対するXML上での拡張属性を作成可能
object CustomEditTextBindingAdapter {
@BindingAdapter("textAttrChanged") // 属性名+AttrChanged
@JvmStatic
fun setTextWatcher(view: CustomEditText?, listener: InverseBindingListener) {
view?.setInverseBindingListener(listener)
}
}
Step.6 拡張Viewに対して双方向DataBindingを指定
private val binding: ActivityMainBinding by lazy {
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.viewModel = viewModel
}
class ViewModel : BaseObservable() {
@Bindable
var textWatcher: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.textWatcher)
}
}
<jp.honkot.CustomEditText
...
app:text="@={viewModel.textWatcher}"/>
こうすると、常に独自Viewの値を監視できます。