Android RxBinding
在先前的教程中,我们讨论了RxJava及其一些运算符。
今天,我们将在Android应用程序中讨论RxBinding库。
Rx绑定
我们知道RxJava是一种基于事件的反应式编程范例。
RxBinding是一组支持库,可简化与Android中的UI元素的用户交互的实现。
要在您的应用程序中使用RxBinding,您必须包括:
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
与RxJava依赖项一起使用:
implementation 'io.reactivex.rxjava2:rxjava:2.1.9' implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
要将RxBinding与Appcompat和其他子模块一起使用,我们只需要导入它们各自的rxbinding库:
implementation com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'implementation 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'compile 'com.jakewharton.rxbinding2:rxbinding-leanback-v17:2.0.0'
我们可以在应用程序中使用RxBinding功能。
RxView.click()
通常,要在Button上设置点击侦听器事件,以下是我们编写的代码段:
Button b = (Button)findViewById(R.id.button);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(),"Button clicked",Toast.LENGTH_SHORT).show();
}
});
现在,使用RxBinding,我们可以执行以下操作:
Disposable d = RxView.clicks(button).
subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) {
Toast.makeText(getApplicationContext(),"Button clicked",Toast.LENGTH_SHORT).show();
}
});
在" RxView.clicks"内部,我们传递了要单击的View实例。
如果您不熟悉RxJava2,则Disposable等同于Subscription。
有关RxJava2中更改的更多信息,请参见此处。
在Disposable实例上,我们可以通过调用d.dispose();取消订阅该事件。
Disposable实例包含对该视图的引用。
因此,如果此代码是在Activity的上下文之外定义的,则必须取消订阅该事件,以防止内存泄漏。
RxView.click()返回一个Observable。
因此,我们可以添加RxJava运算符以在其上执行转换和链式实现。
EditText TextChanges
通常,要在EditText上添加文本更改侦听器,我们需要实现TextWatcher方法:
EditText editText = findViewById(R.id.editText);
editText.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) {
}
});
使用RxBinding,我们可以执行以下操作:
Disposable d2 = RxTextView.textChanges(editText)
.subscribe(new Consumer<CharSequence>() {
@Override
public void accept(CharSequence charSequence) throws Exception {
//Add your logic to work on the Charsequence
}
});
我们在RxTextView.textChanges内部传递EditText。
现在,让我们看看RxJava运算符和转换如何给我们带来影响。
使用地图运算符
使用地图运算符,我们可以更改正在发送的数据。
例如,在EditText中,我们可以将CharSequence更改为String
Disposable d2 = RxTextView.textChanges(editText)
.map(charSequence -> charSequence.toString())
.subscribe(new Consumer<CharSequence>() {
@Override
public void accept(String string) throws Exception {
//Add your logic to work on the Charsequence
}
});
使用反跳运算符
使用反跳运算符,我们可以延迟事件动作。
例如,在"按钮"单击上,我们可以设置2秒的去抖动。
2秒后将运行该操作:
RxView.clicks(button).debounce(2, TimeUnit.SECONDS).
observeOn(AndroidSchedulers.mainThread()).
map(o -> button.getText().toString()).
subscribe(new Consumer<String>() {
@Override
public void accept(String o) throws Exception {
Toast.makeText(getApplicationContext(),o + "was clicked",Toast.LENGTH_SHORT).show();
}
});
反跳操作不在UI线程上运行。
它在计算线程上运行。
因此,如上所述,您必须在observeOn方法中调用主线程。
反跳运算符通常在EditText中使用,尤其是在SearchView中,以使用户在运行操作/请求之前停止键入几秒钟。
使用节流阀
与防抖会延迟动作不同,throttleFirst运算符用于防止在特定时间间隔内重复动作。
ThrottleFirst对于防止重复单击按钮时的双重操作很有用。
RxView.clicks(button).throttleFirst(2, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread()).
subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) {
Toast.makeText(getApplicationContext(), "Avoid multiple clicks using throttleFirst", Toast.LENGTH_SHORT).show();
}
});
在上面的代码中,Button直到2秒后才会再次显示Toast。
合并多个按钮单击动作
我们可以通过以下方式合并RxView.click()Observables:
Button button1 = findViewById(R.id.button1);
Button button2 = findViewById(R.id.button2);
Observable<Object> observable1 = RxView.clicks(button1);
Observable<Object> observable1 = RxView.clicks(button2);
Observable.merge(observable1, observable2)
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) {
//This common logic would be triggered when either of them are clicked
}
});
一个按钮上的多个单击侦听器
CompositeDisposable compositeDisposable = new CompositeDisposable();
Observable<Button> clickObservable = RxView.clicks(button).map(o -> button).share();
Disposable buttonShowToast =
clickObservable.subscribe(new Consumer<Button>() {
@Override
public void accept(Button o) throws Exception {
Toast.makeText(getApplicationContext(), "Show toast", Toast.LENGTH_SHORT).show();
}
});
compositeDisposable.add(buttonShowToast);
Disposable changeButtonText =
clickObservable.subscribe(new Consumer<Button>() {
@Override
public void accept(Button o) throws Exception {
o.setText("New text");
}
});
compositeDisposable.add(changeButtonText);
使用可观察到的RxView.click()上的共享运算符,单击该按钮时,将同时运行下面创建的两个Disposable。
让我们在Android应用程序中将以上概念与更多运算符合并:
代码
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter here!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.35000002"
<TextView
android:id="@+id/txtBelowEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText"
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button2"
app:layout_constraintBaseline_toBaselineOf="@+id/button2"
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Button2"
app:layout_constraintEnd_toStartOf="@+id/button"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
<TextView
android:id="@+id/txtBelowButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button2"
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:layout_marginTop="8dp"
android:src="@android:drawable/ic_input_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Simulateneous Actions"
app:layout_constraintBottom_toTopOf="@+id/fab"
android:layout_margin="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
</android.support.constraint.ConstraintLayout>
MainActivity.java
package com.theitroad.androidrxbinding;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.jakewharton.rxbinding2.view.RxView;
import com.jakewharton.rxbinding2.widget.RxTextView;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
public class MainActivity extends AppCompatActivity {
Button button, button2, button3;
FloatingActionButton fab;
TextView txtBelowEditText, txtBelowButton;
EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
fab = findViewById(R.id.fab);
txtBelowEditText = findViewById(R.id.txtBelowEditText);
txtBelowButton = findViewById(R.id.txtBelowButton);
editText = findViewById(R.id.editText);
Observable<Object> observable1 = RxView.clicks(button2).map(o -> button2);
Observable<Object> observable2 = RxView.clicks(fab).map(o -> fab);
Disposable d1 = Observable.merge(observable1, observable2).throttleFirst(2, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread()).
subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) {
Toast.makeText(getApplicationContext(), "Avoid multiple clicks using throttleFirst", Toast.LENGTH_SHORT).show();
if (o instanceof Button) {
txtBelowButton.setText(((Button) o).getText().toString() + " clicked");
} else if (o instanceof FloatingActionButton) {
txtBelowButton.setText("Fab clicked");
}
}
});
Disposable d = RxView.clicks(button).debounce(5, TimeUnit.SECONDS).
observeOn(AndroidSchedulers.mainThread()).
map(o -> button.getText().toString()).
subscribe(new Consumer<String>() {
@Override
public void accept(String o) throws Exception {
txtBelowButton.setText(o + " was clicked");
}
});
Disposable d2 = RxTextView.textChanges(editText)
.filter(s -> s.toString().length() > 6)
.debounce(2, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<CharSequence>() {
@Override
public void accept(CharSequence charSequence) throws Exception {
txtBelowEditText.setText(charSequence);
}
});
CompositeDisposable compositeDisposable = new CompositeDisposable();
Observable<Button> clickObservable = RxView.clicks(button3).map(o -> button3).share();
Disposable buttonShowToast =
clickObservable.subscribe(new Consumer<Button>() {
@Override
public void accept(Button o) throws Exception {
Toast.makeText(getApplicationContext(), "Show toast", Toast.LENGTH_SHORT).show();
}
});
compositeDisposable.add(buttonShowToast);
Disposable changeButtonText =
clickObservable.subscribe(new Consumer<Button>() {
@Override
public void accept(Button o) throws Exception {
o.setText("New text");
}
});
compositeDisposable.add(changeButtonText);
}
}
在EditText中,我们设置了一个filter运算符,直到长度超过阈值时才将输入文本设置到TextView上。
要清除所有Disposables,而不是分别对它们进行一次性调用,我们还可以执行以下操作:
CompositeDisposable clearAllDisposables = new CompositeDisposable();
clearAllDisposables.add(d1);
clearAllDisposables.add(d2);
clearAllDisposables.add(d);
clearAllDisposables.clear();
实际应用程序的输出如下:
注意单击FloatingActionButton的次数。
但是,吐司只被展示了一次。

