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

Gob*_*ber 44 android android-edittext android-databinding

我已经实现了新的Android数据绑定,并在实现后意识到它不支持双向绑定.我试图手动解决这个问题,但我很难找到一个在绑定到EditText时使用的好解决方案.在我的布局中,我有这样的观点:

<EditText
android:id="@+id/firstname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapWords|textNoSuggestions"
android:text="@{statement.firstName}"/>
Run Code Online (Sandbox Code Playgroud)

另一种观点也显示了结果:

<TextView
style="@style/Text.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{statement.firstName}"/>
Run Code Online (Sandbox Code Playgroud)

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

FragmentStatementPersonaliaBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_statement_personalia, container, false);
binding.setStatement(mCurrentStatement);
Run Code Online (Sandbox Code Playgroud)

这样可以将firstName的当前值放在EditText中.问题是如何在文本更改时更新模型.我尝试在editText上放置一个OnTextChanged-listener并更新模型.这创建了一个循环杀死我的应用程序(模型更新更新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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

这样做效果更好,但每次我写一封信时,GUI都会更新,因为编辑光标会移到前面.

欢迎大家提出意见

Gob*_*ber 83

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

android:text="@{viewModel.address}"
Run Code Online (Sandbox Code Playgroud)

有:

android:text="@={viewModel.address}"
Run Code Online (Sandbox Code Playgroud)

例如,在EditText中,您将获得双向绑定.确保您更新到最新版本的Android Studio/gradle/build-tools以启用此功能.

(以前的答案):

我尝试了Bhavdip Pathar的解决方案,但这无法更新我绑定到同一个变量的其他视图.我通过创建自己的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);
}}
Run Code Online (Sandbox Code Playgroud)

使用此解决方案,您可以以任何方式更新模型(TextWatcher,OnTextChangedListener等),并为您处理无限更新循环.使用此解决方案,模型设置器可以简单地实现为:

public void setFirstName(String firstName) {
    this.firstName = firstName;
    notifyPropertyChanged(BR.firstName);
}
Run Code Online (Sandbox Code Playgroud)

这样可以减少模型类中的代码(您可以将侦听器保留在Fragment中).

我将不胜感激任何评论,改进或其他/更好的解决方案

  • 我想知道为什么google在他们的文档页面https://developer.android.com/topic/libraries/data-binding/index.html上没有这个?你能和我分享一些有关Android数据绑定的文件吗? (4认同)

Jer*_*all 19

使用gradle插件2.1+时,Android Studio 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}"/>
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅:https://halfthought.wordpress.com/2016/03/23/2-way-data-binding-on-android/


Bha*_*gar 7

@Gober android数据绑定支持双向绑定.因此,您无需手动进行.当您尝试将OnTextChanged-listener放在editText上时.它应该更新模型.

我尝试在editText上放置一个OnTextChanged-listener并更新模型.这创建了一个循环杀死我的应用程序(模型更新更新GUI,调用textChanged时间无穷大).

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

以下是修改后的视图模型示例,如果更改源自观察程序,则不会引发数据绑定通知:

让我们创建一个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);
}
Run Code Online (Sandbox Code Playgroud)

接下来,在视图模型中,我们可以创建一个公开观察者的方法.观察程序将配置为将更改的控件值传递给视图模型:

@Bindable
public TextWatcher getOnUsernameChanged() {

    return new SimpleTextWatcher() {
        @Override
        public void onTextChanged(String newValue) {
            setUsername(newValue);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

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

<!-- most attributes removed -->
<EditText
    android:id="@+id/input_username"
    android:addTextChangedListener="@{viewModel.onUsernameChanged}"/>
Run Code Online (Sandbox Code Playgroud)

以下是解决通知无限的视图模型的实现.

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;
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望这是你正在寻找的,当然可以帮助你.谢谢

  • 谢谢你的全面答复.它看起来像一个可行的解决方案,但不幸的是它不适用于我的用例.首先,Android数据绑定本身不支持双向绑定,因为你和我自己都清楚地实现了这一点.其次,我对此解决方案的问题是其他视图也绑定到同一个变量.我有一个更新数据的EditText和一个输出相同数据的TextView.使用此解决方案,TextView不会更新.但是如果你只有一个绑定变量,这是一个很好的解决方案! (2认同)