使用Android数据绑定创build双向绑定

我已经实现了新的Android数据绑定,并且实现后意识到它不支持双向绑定。 我试图解决这个手动,但我努力寻找一个好的解决scheme时使用绑定到EditText。 在我的布局中,我有这样的观点:

<EditText android:id="@+id/firstname" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textCapWords|textNoSuggestions" android:text="@{statement.firstName}"/> 

另一种看法也是显示结果:

 <TextView style="@style/Text.Large" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{statement.firstName}"/> 

在我的片段中,我创build了这样的绑定:

 FragmentStatementPersonaliaBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_statement_personalia, container, false); binding.setStatement(mCurrentStatement); 

这个工作,并把当前值firstName在EditText中。 问题是如何在文本更改时更新模型。 我试着在EditText上放置一个OnTextChanged监听器并更新模型。 这创build了一个循环杀死我的应用程序(模型更新更新GUI,它调用textChanged时间无限)。 接下来,我试图只在发生如下真实变化时通知:

 @Bindable public String getFirstName() { return firstName; } public void setFirstName(String firstName) { boolean changed = !TextUtils.equals(this.firstName, firstName); this.firstName = firstName; if(changed) { notifyPropertyChanged(BR.firstName); } } 

这样做效果更好,但是每次我写一封信时,GUI都会更新,出于某种原因,编辑光标会移到最前面。

欢迎大家提出意见

编辑04.05.16:Android数据绑定现在自动支持双向绑定! 简单地replace:

 android:text="@{viewModel.address}" 

有:

 android:text="@={viewModel.address}" 

在一个EditText例如,你得到双向绑定。 确保你更新到最新版本的Android Studio / gradle / build-tools来启用它。

(以前的答案):

我尝试了Bhavdip Pathar的解决scheme,但是这无法更新我绑定到同一个variables的其他视图。 我通过创build自己的EditText来解决这个问题:

 public class BindableEditText extends EditText{ public BindableEditText(Context context) { super(context); } public BindableEditText(Context context, AttributeSet attrs) { super(context, attrs); } public BindableEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } private boolean isInititalized = false; @Override public void setText(CharSequence text, BufferType type) { //Initialization if(!isInititalized){ super.setText(text, type); if(type == BufferType.EDITABLE){ isInititalized = true; } return; } //No change if(TextUtils.equals(getText(), text)){ return; } //Change int prevCaretPosition = getSelectionEnd(); super.setText(text, type); setSelection(prevCaretPosition); }} 

有了这个解决scheme,你可以任何你想要的方式更新模型(TextWatcher,OnTextChangedListener等),它会照顾你的无限更新循环。 有了这个解决scheme,模型制定者可以简单地实现为:

 public void setFirstName(String firstName) { this.firstName = firstName; notifyPropertyChanged(BR.firstName); } 

这会减less模型类中的代码(您可以将侦听器保留在您的Fragment中)。

我将不胜感激任何意见,改进或其他/更好的解决scheme,我的问题

现在,Android Studio 2.1+支持使用gradle插件2.1+

只需将EditText的文本属性从@{}更改为@={}如下所示:

 <EditText android:id="@+id/firstname" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textCapWords|textNoSuggestions" android:text="@={statement.firstName}"/> 

欲了解更多信息,请参阅: https : //halfthought.wordpress.com/2016/03/23/2-way-data-binding-on-android/

@Gober android数据绑定支持双向绑定。 所以你不需要手动。 正如你试图把OnTextChanged-listener放在editText上一样。 它应该更新模型。

我试着在EditText上放置一个OnTextChanged监听器并更新模型。 这创build了一个循环杀死我的应用程序(模型更新更新GUI,它调用textChanged时间无限)。

值得注意的是,实现双向绑定的绑定框架通常会为您检查

以下是修改视图模型的示例,如果更改始于观察器,则不会引发数据绑定通知:

我们来创build一个只需要重写一个方法的SimpleTextWatcher:

 public abstract class SimpleTextWatcher implements TextWatcher { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { onTextChanged(s.toString()); } public abstract void onTextChanged(String newValue); } 

接下来,在视图模型中,我们可以创build一个公开观察者的方法。 监视器将被configuration为将更改的控件值传递给视图模型:

 @Bindable public TextWatcher getOnUsernameChanged() { return new SimpleTextWatcher() { @Override public void onTextChanged(String newValue) { setUsername(newValue); } }; } 

最后,在视图中,我们可以使用addTextChangeListener将观察者绑定到EditText:

 <!-- most attributes removed --> <EditText android:id="@+id/input_username" android:addTextChangedListener="@{viewModel.onUsernameChanged}"/> 

这里是解决通知无限的视图模型的实现。

 public class LoginViewModel extends BaseObservable { private String username; private String password; private boolean isInNotification = false; private Command loginCommand; public LoginViewModel(){ loginCommand = new Command() { @Override public void onExecute() { Log.d("db", String.format("username=%s;password=%s", username, password)); } }; } @Bindable public String getUsername() { return this.username; } @Bindable public String getPassword() { return this.password; } public Command getLoginCommand() { return loginCommand; } public void setUsername(String username) { this.username = username; if (!isInNotification) notifyPropertyChanged(com.petermajor.databinding.BR.username); } public void setPassword(String password) { this.password = password; if (!isInNotification) notifyPropertyChanged(com.petermajor.databinding.BR.password); } @Bindable public TextWatcher getOnUsernameChanged() { return new SimpleTextWatcher() { @Override public void onTextChanged(String newValue) { isInNotification = true; setUsername(newValue); isInNotification = false; } }; } @Bindable public TextWatcher getOnPasswordChanged() { return new SimpleTextWatcher() { @Override public void onTextChanged(String newValue) { isInNotification = true; setPassword(newValue); isInNotification = false; } }; } } 

我希望这是你正在寻找,肯定可以帮助你。 谢谢

POJO:

 public class User { public final ObservableField<String> firstName = new ObservableField<>(); public final ObservableField<String> lastName = new ObservableField<>(); public User(String firstName, String lastName) { this.firstName.set(firstName); this.lastName.set(lastName); } public TextWatcherAdapter firstNameWatcher = new TextWatcherAdapter(firstName); public TextWatcherAdapter lastNameWatcher = new TextWatcherAdapter(lastName); } 

布局:

  <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName, default=First_NAME}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName, default=LAST_NAME}"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editFirstName" android:text="@{user.firstNameWatcher.value}" android:addTextChangedListener="@{user.firstNameWatcher}"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/editLastName" android:text="@{user.lastNameWatcher.value}" android:addTextChangedListener="@{user.lastNameWatcher}"/> 

守望者:

 public class TextWatcherAdapter implements TextWatcher { public final ObservableField<String> value = new ObservableField<>(); private final ObservableField<String> field; private boolean isInEditMode = false; public TextWatcherAdapter(ObservableField<String> f) { this.field = f; field.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback(){ @Override public void onPropertyChanged(Observable sender, int propertyId) { if (isInEditMode){ return; } value.set(field.get()); } }); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // } @Override public void afterTextChanged(Editable s) { if (!Objects.equals(field.get(), s.toString())) { isInEditMode = true; field.set(s.toString()); isInEditMode = false; } } } 

有一个更简单的解决scheme。 如果没有真正改变,就避免更新字段。

 @Bindable public String getFirstName() { return firstName; } public void setFirstName(String firstName) { if(this.firstName.equals(firstName)) return; this.firstName = firstName; notifyPropertyChanged(BR.firstName); }