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()了,这样就避免了一直调用.

猜你喜欢

转载自blog.csdn.net/MoLiao2046/article/details/107977255