项目中用到组合控件, 这个控件用的特别的多, 但还想用DataBinding的双向绑定功能, 很多博客有写实现步骤, 这里面涉及了那些逻辑呢, 这篇笔记是我使用时候的总结.
我在自己写的组合控件中用到了双向绑定,有兴趣可以看下.项目
控件介绍
组合控件中有一个EditText
,用户向EditText
输入新的内容时,MutableLiveData
可以收到输入的内容,通过向MutableLiveData
中setValue()
新数据能实现主动更新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()
了,这样就避免了一直调用.