2015-10-27 79 views
38

我已經實現了新的Android數據綁定,並且在實現後意識到它不支持雙向綁定。我試圖手動解決這個問題,但我很努力地找到一個很好的解決方案,當綁定到EditText時使用。 在我的佈局,我有這樣的看法:創建與Android數據綁定的雙向綁定

<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}"/> 

在我的片段,我創建了這樣的結合:

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

該作品並在EditText中放入firstName的當前值。問題在於文本更改時如何更新模型。我試着在EditText上放置一個OnTextChanged監聽器並更新模型。這創建了一個循環來殺死我的應用程序(模型更新更新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); 
     } 
    } 

這個工作好,但每次我寫信,圖形用戶界面的更新和對索姆原因編輯光標移動到前面。

任何建議將受到歡迎

+0

你的吸氣劑在哪裏?你給它添加了@ @ Bindable'註解嗎? –

+0

是的。現在將吸氣劑添加到描述中。 – Gober

+0

儘管上面有布爾值,你總是調用'this.firstName = firstName'。你看過那個邏輯嗎? –

回答

73

編輯04.05。16: Android數據綁定現在自動支持雙向綁定! 只需更換:

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

有:

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

在一個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); 
}} 

有了這個解決方案,您可以更新模型,你想要的(TextWatcher,OnTextChangedListener等)的任何方式,它需要照顧的無限更新循環的你。這種解決方案的模式制定者可以簡單地實現爲:

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

這讓更少的代碼模型類(你可以在你的片段中的聽衆)。

我將不勝感激任何意見,改進或其它/更好的解決方案,以我的問題

+1

你實際上是否使用數據綁定將文本設置到編輯框?我在你的實際問題中看到 –

+0

我想你還需要在'View'的'BaseSavedState'中保存'isInititalized'成員。 – WindRider

+4

我想知道爲什麼谷歌沒有這樣的文檔頁面https://developer.android.com/topic/libraries/data-binding/index.html? 你能跟我分享一些關於Android數據綁定的文檔嗎? – rocketspacer

6

@Gober Android的數據綁定支持雙向綁定。因此你不需要手動。正如您試圖將OnTextChanged-listener放在editText上一樣。它應該更新模型。

我試着把OnTextChanged-listener放在editText上並更新 模型。這創建了一個循環來殺死我的應用程序(模型更新更新 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); 
} 

接下來,在視圖模型我們可以創建一個公開觀察者的方法。觀察者將被配置爲在控制的改變值傳遞給視圖模型:

@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; 
      } 
     }; 
    } 
} 

我希望這是你正在尋找,肯定可以幫助你。謝謝

+2

謝謝您的詳細解答。它看起來像一個可行的解決方案,但不幸的是它不適用於我的用例。 首先,Android數據綁定本身不支持雙向綁定,因爲你和我都清楚地實現了這一點。 其次,我用這個解決方案的問題是,其他視圖也綁定到相同的變量。我有一個更新數據的EditText和一個輸出相同數據的TextView。有了這個解決方案,TextView不會更新。 但是,如果你只有一個綁定到變量,這是一個很好的解決方案! – Gober

+1

嗨!當我將它添加到佈局xml時,Android Studio會說「未知屬性android:addTextChangedListener」。還有什麼可以做的嗎?謝謝。 – wangzhangjian

+0

它應該在佈局xml文件中工作。 –

2

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; 
     } 
    } 

} 
+2

不鼓勵代碼轉儲 - 請提供一些解釋以幫助提高此答案的質量。 – CubeJockey

2

該重新是一個更簡單的解決方案。如果它沒有真正改變,請避免更新字段。

@Bindable 
public String getFirstName() { 
    return firstName; 
} 

public void setFirstName(String firstName) { 
    if(this.firstName.equals(firstName)) 
     return; 

    this.firstName = firstName; 
    notifyPropertyChanged(BR.firstName); 
} 
+1

請參閱我原來的問題的底部。這是我第一次嘗試,但每次有變化(即寫每個字母)光標跳轉到編輯文本輸入的前面。 – Gober

+0

我最終使用的解決方案基本上是這樣的,但之後再次將光標移動到末尾 – Gober

+0

裏面的recylclerview?好吧,不幸的是,它似乎沒有 –

18

這現在支持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/

+0

感覺就像Angular一樣。雙向綁定可以緩解發展。 –