Android 自定义View时添加DataBinding双向绑定功能

项目中用到组合控件, 这个控件用的特别的多, 但还想用DataBinding的双向绑定功能, 很多博客有写实现步骤, 这里面涉及了那些逻辑呢, 这篇笔记是我使用时候的总结.
我在自己写的组合控件中用到了双向绑定,有兴趣可以看下.项目

控件介绍

组合控件中有一个EditText,用户向EditText输入新的内容时,MutableLiveData可以收到输入的内容,通过向MutableLiveDatasetValue()新数据能实现主动更新EditText的内容.

  • 组合控件代码
public class CustomeEditTextView extends FrameLayout {
    private EditText mEtContent;
     // SET 方法
     @BindingAdapter("y_content")
     public static void setStr(CustomeEditTextView cetv, String content) {
		if (cetv != null) {
		    String mCurrentStr = cetv.mEtContent.getText().toString().trim();
		    if (!TextUtils.isEmpty(content)) {
		        if (!content.equalsIgnoreCase(mCurrentStr)) {
		            cetv.mEtContent.setText(content);
		            // 设置光标位置
		            cetv.mEtContent.setSelection(content.length());
		        }
		    }
}
     }
     // GET 方法
     @InverseBindingAdapter(attribute = "y_content", event = "contentAttrChanged")
     public static String getStr(CustomeEditTextView cetv) {
         return cetv.mEtContent.getText().toString().trim();
     }
     // 监听,如果有变动就调用listener中的onChange方法
     @BindingAdapter(value = "contentAttrChanged")
     public static void setChangeListener(CustomeEditTextView cetv, InverseBindingListener listener) {
         cetv.mEtContent.addTextChangedListener(new TextWatcher() {
             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
             }
             @Override
             public void onTextChanged(CharSequence s, int start, int before, int count) {
                 listener.onChange();
             }
             @Override
             public void afterTextChanged(Editable s) {
             }
         });
     }
}
  • XML布局中使用
<data>
    <variable
        name="mVM"
        type="com.yey.MyVM" />
</data>
<com.yey.ycustomeview.CustomeEditTextView
    android:id="@+id/cetv_1"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    app:y_content="@={mVM.mContentMLD1}"/>
  • 使用代码
// MyVM.java
public class MyVM extends ViewModel { 
    public MutableLiveData<String> mContentMLD1;
    public MyVM() {
         mContentMLD1 = new MutableLiveData<>();
    }
}
// MainActivity.java
public class MainActivity extends AppCompatActivity {
    private MyVM myVM;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        initVM();
    }
    private void initVM() {
        myVM = new ViewModelProvider(this).get(MyVM.class);
        mainBinding.setMVM(myVM);
        myVM.mContentMLD1.setValue("1");
    }
}

以上的这些就是使用以及实现的步骤了,实现时候的三个方法是不是看起来毫无头绪,下面接着看.

内在逻辑分析
myVM.mContentMLD1.setValue("1")
  • 当程序调用myVM.mContentMLD1.setValue("1")后,最终会调用ActivityMainBindingImpl.executeBindings()方法,这个方法中会调用组合控件中定义的setStr(CustomeEditTextView cetv, String content)方法.
public class ActivityMainBindingImpl extends ActivityMainBinding  {
    @Override
    protected void executeBindings() {
        //1. read mVM.mContentMLD1 这里是获取MyVM中的MutableLiveData类型的mContentMLD1对象
        mVMMContentMLD1 = mVM.mContentMLD1;
        //2. 获取mContentMLD1中的值
        mVMMContentMLD1GetValue = mVMMContentMLD1.getValue();
        //3. 调用组合控件中的setStr()方法,最终将mVMMContentMLD1中的值取出后赋值给EditText对象.
        com.yey.ycustomeview.CustomeEditTextView.setStr(this.cetv1, mVMMContentMLD1GetValue);
    }
}
InverseBindingListener.onChange();
  • 在组合控件中对EditText输入改变做了监听,每当输入改变的时候就会调用InverseBindingListener.onChange();,下面看下onChange()方法的具体实现
public class ActivityMainBindingImpl extends ActivityMainBinding  {
    private androidx.databinding.InverseBindingListener cetv1contentAttrChanged = new androidx.databinding.InverseBindingListener() {
        @Override
        public void onChange() {
            // 通过组合控件中的getStr方法获取EditText当前的内容
            java.lang.String callbackArg_0 = com.yey.ycustomeview.CustomeEditTextView.getStr(cetv1);
            // 获取MyVM中的MutableLiveData类型的mContentMLD1对象
            mVMMContentMLD1 = mVM.mContentMLD1;
            // 将EditText当前的内容设置到mContentMLD1对象中
            mVMMContentMLD1.setValue(((java.lang.String) (callbackArg_0)));
        }
    }
}
  • setChangeListener(CustomeEditTextView cetv, InverseBindingListener listener)何时被调用?
    ActivityMainBindingImpl.executeBindings()被调用的时候会执行setChangeListener(CustomeEditTextView cetv, InverseBindingListener listener)`方法.
public class ActivityMainBindingImpl extends ActivityMainBinding  {
    @Override
    protected void executeBindings() {
        com.yey.ycustomeview.CustomeEditTextView.setChangeListener(this.cetv1, cetv1contentAttrChanged);
    }
}
注意

如果看到这里会发现有一个无限调用的闭环,EditText.setText()->TextWatcher.onTextChanged()->InverseBindingListener.onChange()->MutableLiveData.setValue()->EditText.setText(),所以在setStr()方法中判断下如果新的数据内容与当前EditText中的内容一样,就别再执行EditText.setText()了,这样就避免了一直调用.

<think>好的,我现在需要回答用户关于Android DataBinding双向绑定机制和实现原理的问题。首先,我得回忆一下DataBinding的基本概念,然后深入探讨双向绑定的部分。 首先,用户提到了DataBinding双向绑定,所以需要明确什么是双向绑定。单向绑定是数据从模型流向视图,而双向绑定允许视图的变化也能自动更新模型中的数据。这在像EditText这样的输入控件中非常有用,用户输入后数据能自动更新到对应的数据模型。 接下来,我需要解释双向绑定的实现原理。可能涉及以下几个部分: 1. **数据观察机制**:DataBinding库使用观察者模式,当数据变化通知视图更新,反之亦然。可能使用BaseObservable和Bindable注解,或者使用ObservableField等类。 2. **双向绑定的语法**:在布局文件中使用@={}符号,例如android:text="@={viewModel.userName}",这里的等号表示双向绑定。 3. **事件监听**:视图的变化如何触发数据更新。例如,EditText的TextWatcher监听文本变化,然后更新数据模型。 4. **底层实现**:可能涉及到生成的绑定类,这些类如何处理双向的数据流。比如,如何自动为控件添加监听器,并在数据变化更新双方。 然后,需要确保回答结构清晰,分点说明。可能还需要引用一些官方文档或可靠资料来支持解释,但用户提供的引用似乎与Kotlin和Java生态相关,可能不太直接相关,但如果有合适的部分可以引用的话。 还需要注意用户提到的系统级指令,比如数学公式要用$...$,独立公式用$$...$$,但这个问题可能不太涉及数学公式,但如果有涉及的地方需要注意格式。此外,用中文回答,生成相关问题。 现在,需要组织这些思路成一个连贯的回答,分步骤说明双向绑定的机制,可能包括数据观察、绑定适配器、生成的代码等部分。同,可能需要提到InverseBindingAdapter,因为这在双向绑定中用于处理反向的数据流,即从视图到模型。 还需要检查是否有遗漏的部分,例如数据变更的通知机制,比如ObservableField的使用,或者LiveData在DataBinding中的整合,但用户的问题可能集中在双向绑定的基础原理上,所以可能不需要涉及LiveData,除非它相关。 最后,生成相关问题,例如如何自定义双向绑定,常见问题有哪些,或者与单向绑定的区别等。</think>### Android DataBinding双向绑定机制及实现原理 Android DataBinding双向绑定允许**视图与数据模型之间的自动同步**:当数据变化更新视图,当视图内容变化自动回写数据。其核心实现分为以下几个步骤: --- #### 1. **数据观察机制** - **数据层**:使用`Observable`接口(如`BaseObservable`类)或`ObservableField`包装数据。 示例: ```kotlin class User : BaseObservable() { @Bindable var name: String = "" set(value) { field = value notifyPropertyChanged(BR.name) } } ``` 通过`@Bindable`注解和`notifyPropertyChanged()`方法通知绑定类数据变化[^1]。 - **视图层**:DataBinding自动生成的绑定类(如`ActivityMainBinding`)监听数据变化,并更新对应视图。 --- #### 2. **双向绑定语法** 在布局文件中使用`@={}`符号建立双向绑定: ```xml <EditText android:text="@={viewModel.user.name}" /> ``` 这里的`@=`表示双向绑定,而`@`仅表示单向绑定。 --- #### 3. **反向绑定适配器(Inverse Binding Adapter)** 双向绑定的关键在于**处理视图到数据的反向更新**: - **正向绑定**:数据 → 视图,通过`@BindingAdapter`实现。 - **反向绑定**:视图 → 数据,通过`@InverseBindingAdapter`实现。 以`EditText`的文本绑定为例: ```java // 正向:将数据设置到视图 @BindingAdapter("android:text") fun setText(view: EditText, value: String?) { if (view.text.toString() != value) { view.setText(value) } } // 反向:从视图读取数据 @InverseBindingAdapter(attribute = "android:text") fun getText(view: EditText): String { return view.text.toString() } ``` --- #### 4. **事件监听与更新触发** DataBinding自动为视图添加事件监听器。例如,`EditText`会注册`TextWatcher`: ```java // 自动生成的代码片段 editText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { inverseBindingListener.onChange(); // 触发反向更新 } }); ``` 当用户输入文本,`inverseBindingListener`会调用反向绑定适配器,将新值写回数据模型。 --- #### 5. **底层实现流程** 1. **编译**:APT生成绑定类,解析`@={}`并生成正向/反向绑定逻辑。 2. **运行**: - 数据变化 → 通过`notifyPropertyChanged()`通知绑定类 → 更新视图。 - 视图变化 → 通过事件监听触发反向绑定 → 更新数据模型。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值