2

我只是在学习RxJavaAndroid 和(假定的)MVVM、数据绑定和 RxJava 的优秀组合。不幸的是,我不能将 anRxJava Observable直接绑定到 a View:需要 a LiveData

所以,我想知道有没有办法用 RxJava 实现双向数据绑定?

到目前为止,我已经尝试编写一个BindingAdapter在传递的View和调用onNext上添加一个侦听器的Subject.

@BindingAdapter("rxText")
public static void bindReactiveText(EditText view, BehaviorSubject<String> text)
{
    view.setText(text.getValue());

    view.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) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            text.onNext(s.toString());
        }
    });
}

Subject这样做是用 中的任何更改更新/model View,因此 model 始终具有与 . 一致的值View。然而,绑定不会因为自身的任何变化而触发Subject(当然我们必须比较新旧值才能停止循环)。

现在,我确实尝试订阅主题并调用setText每个发射,但随后我必须处理Observer. 所以我所做的也是监听 View Attach State 的变化:订阅onViewAttachedToWindow并在onViewDetachedFromWindow.

@BindingAdapter("rxText")
public static void bindReactiveText(EditText editText, BehaviorSubject<String> text)
{
    setText(editText, text.getValue());

    editText.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
        private TextWatcher textWatcher = 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) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                text.onNext(s.toString());
            }
        };

        private Disposable disposable;

        @Override
        public void onViewAttachedToWindow(View v) {
            editText.addTextChangedListener(textWatcher);

            disposable = text.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .filter(s -> !s.equals(editText.getText().toString()))
                    .subscribe(s -> setText(editText, text.getValue()));
        }

        @Override
        public void onViewDetachedFromWindow(View v) {
            disposable.dispose();
            editText.removeTextChangedListener(textWatcher);
        }
    });
}

虽然这确实以预期的方式工作,但我不确定这是否是通过 RxJava 实现双向绑定的最佳方法。

想到的直接缺点之一是,Activity/Fragment无法为大多数 android 注册回调Views。例如,如果我对 Button 及其单击使用相同的方法,并在我Activity的绑定中设置一个侦听器,则绑定将停止工作。

我仍在学习 RxJava 及其各种运算符及其用途,所以也许我遗漏了一些明显的东西或犯了一个错误,但几天来我一直在尝试另一种方法来做到这一点,到目前为止还没有能想到一个。

所以我的问题是:用 RxJava 实现双向数据绑定的最佳方法是什么Observables

4

2 回答 2

1

要将 RxJava Observable 直接绑定到视图,您需要使用ObservableField

val name = ObservableField<String>()
//ex: name = "john"

然后把它放在你的xml中:

<data>
     <import type="android.databinding.ObservableField"/>
     <variable 
         name="name" 
         type="ObservableField<String>" />
</data>

<TextView
     android:text="@{name}"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"/>

参考:https ://developer.android.com/topic/libraries/data-binding/observability

编辑:

如果您需要将其应用于模型而不是单个字段,您可以这样做:

class User : BaseObservable() {

    @get:Bindable
    var firstName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.lastName)
        }
}

BR是 Android 数据绑定生成的类的名称。

于 2020-02-24T10:44:33.257 回答
0

我不建议你那样做。虽然 Rx Java 与数据绑定的使用很好(虽然 LiveData 更好),但您实现它的方式相当过度。

这足以将其绑定到 textView。

@BindingAdapter("rxText")
public static void bindReactiveText(EditText view, BehaviorSubject<String> text)
{
    view.setText(text.getValue());

    view.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) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            text.onNext(s.toString());
        }
    });
}

要订阅并使用它,您应该使用一些不同的方法。您应该在看起来像这样的视图模型中使用它

public class MyViewModel extends ViewModel {
    private CompositeDisposable subscriptions = new CompositeDisposable();
    public BehaviorSubject<String> text = new BehaviorSubject<String>();

    private void bind() {
      subscriptions.add(
        text.subscribe(text -> {
            //do something
        })
      )
    }

    @Override
    public void onCleared(){
      subscriptions.clear()
    }
}

并在布局使用中

...
    <TextView
        android:id="@+id/text"
        style="@style/TextField.Subtitle3.Primary"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/small_margin"
        app:rxText="@{vm.text}"
    />
...

并在片段中

public class MasterFragment extends Fragment {
    private MyViewModel vm;

    public void onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        ViewDataBinding binding = DataBindingUtil.inflate(inflater, layoutRes, container, false);
        model = new ViewModelProvider(requireActivity()).get(MyViewModel.class);
        binding.setVariable(BR.vm, this);
        binding.executePendingBindings();
        vm.bind();
        return binding.root;
    }
}

如果我为此使用 RxJava,我会做这样的事情。我正在使用 LiveData。

希望能帮助到你。

于 2020-02-24T10:34:37.813 回答