LoginSignup
4
4

More than 3 years have passed since last update.

独自Viewに双方向DataBinding

Posted at

欲しいなぁと思っていた機能が簡単に実装可能なのでメモ


はじめに

この記事は独自に実装したViewに対して、
EditText.textへの双方向DataBindingのようなものを実装するための方法を記載します

            <EditText
                    ...
                    android:text="@={viewModel.text}" />

これを、こうして双方向DataBindingする方法

            <jp.honkot.CustomizedEditText
                    ...
                    app:text="@={viewModel.text}" />

なお、この例では初歩的なBaseObservableを利用していますが、
ObservableFieldやMutableLiveDataでも代用可能です。

前提

  • DataBindingの利用方法を心得ている
  • 双方向DataBindingの使い方を心得ている

実装イメージ

図にするとこんな感じ

Screen Shot 2019-07-22 at 12.41.04.png

実装

順を追って実装しましょう

Step.1 独自Viewの実装

割愛しますが、まぁこんな感じで書くと思います

CustomizedEditText.kt
class CustomEditText @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
    ...
}

Step.2 InverseBindingMethodsアノテーションを指定

独自ViewクラスにInverseBindingMethodsアノテーションを実装します
このattributeで指定した属性名がそのままXMLファイル上で双方向DataBindingで指定する属性になります

CustomizedEditText.kt
@InverseBindingMethods(
    InverseBindingMethod(
        type = CustomEditText::class,  // 対象のクラス
        attribute = "text"  // 作成する属性
    )
)
class CustomEditText @JvmOverloads constructor(

Step.3 Getter/Setterを用意

指定した双方向DataBindingでやりとりするGetter/Setterを指定
なお、ここではCustomizedEditTextの中でもViewModelが存在し、
その中の属性も双方向DataBindingをしていると仮定する

CustomizedEditText.kt
    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に持たせます

CustomizedEditText.kt
    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上での拡張属性を作成可能

CustomizedEditText.kt
    object CustomEditTextBindingAdapter {
        @BindingAdapter("textAttrChanged") // 属性名+AttrChanged
        @JvmStatic
        fun setTextWatcher(view: CustomEditText?, listener: InverseBindingListener) {
            view?.setInverseBindingListener(listener)
        }
    }

Step.6 拡張Viewに対して双方向DataBindingを指定

MainActivity.kt
    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)
            }
    }
activity_main.xml
            <jp.honkot.CustomEditText
                    ...
                    app:text="@={viewModel.textWatcher}"/>

こうすると、常に独自Viewの値を監視できます。

4
4
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
4
4