2015-10-27 13 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); 

это работает и помещает текущее значение firstName в EditText. Проблема заключается в том, как обновить модель при изменении текста. Я попробовал поставить OnTextChanged-listener на editText и обновить модель. Это создало цикл, убивающий мое приложение (обновление модели обновляет графический интерфейс, который вызывает textChanged times бесконечность). Затем я попытался только уведомить, когда реальные изменения произошли как это:

@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', несмотря на то, что над ним находится boolean. Вы заглянули в эту логику? –

ответ

73

EDIT 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

Я полагаю, вам также нужно сохранить член 'isInititalized' в' Base'SaveState' 'View'. – WindRider

+4

Интересно, почему google не имеет этого на странице своего документа https://developer.android.com/topic/libraries/data-binding/index.html? Можете ли вы поделиться со мной некоторыми документами по привязке данных Android? – rocketspacer

6

@Gober андроида поддержка связывания данных двух способов связывания. Поэтому вам не нужно делать это вручную. Как вы пробовали, поставив OnTextChanged-listener на editText. Он должен обновить модель.

Я попытался положить OnTextChanged-прослушиватель на editText и обновить модель . Это создало цикл, убивающий мое приложение (обновления модели обновления GUI, который вызывает textChanged times бесконечность).

Стоит отметить, что обязательные рамки, которые реализуют двухсторонние связывания обычно делаю эту проверку для вас ...

Вот пример модифицированной модели вида, который не вызывает уведомление связывания данных, если изменение произошло в наблюдателе:

Давайте создадим 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); 
     } 
    }; 
} 

Наконец, с точки зрения мы можем связать Бодрствующего к EditText с помощью addTextChangeListener:

<!-- 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

Привет! Android Studio сообщила, что «неизвестный атрибут android: addTextChangedListener», когда я добавляю его в макет xml. Есть ли что-то еще, чтобы заставить его работать? Благодарю. – 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}"/> 

Watcher:

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

re - более простое решение. Просто не обновляйте поле, если оно действительно не изменилось.

@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

Похоже на Угловое. Двусторонняя привязка облегчает разработку. –

Смежные вопросы