In the previous tutorials, we discussed RxJava and some of its operators. Today we will discuss the RxBinding library in our Android Application.
在之前的教程中,我们讨论了RxJava及其一些运算符 。 今天,我们将在Android应用程序中讨论RxBinding库。
Rx绑定 (RxBinding)
We know that RxJava is a reactive event-based programming paradigm.
RxBinding is a set of support libraries to make the implementation of user interaction with the UI elements in Android easier.
我们知道RxJava是一种基于事件的React式编程范例。
RxBinding是一组支持库,可简化与Android中的UI元素的用户交互的实现。
To use RxBinding in your application you must include:
要在您的应用程序中使用RxBinding,您必须包括:
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
Using this along with the RxJava dependency:
与RxJava依赖项一起使用:
implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
To use RxBinding with Appcompat and other submodules we simply need to import their respective rxbinding libraries:
要将RxBinding与Appcompat和其他子模块一起使用,我们只需要导入它们各自的rxbinding库:
implementation com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
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-support-v4:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7: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-design:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-leanback-v17:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-leanback-v17:2.0.0'
we can use RxBinding features in our application.
我们可以在应用程序中使用RxBinding功能。
RxView.click() (RxView.click())
Typically, to set click listener events on a Button, the following is the piece of code that we write:
通常,要在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();
}
});
Now with RxBinding we can do:
现在,使用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();
}
});
Inside RxView.clicks
we pass the View instance which is to be clicked.
在RxView.clicks
内部,我们传递了要单击的View实例。
If you are new to RxJava2, Disposable is equivalent to Subscription.
For more information on changes in RxJava2 refer here.
如果您不熟悉RxJava2,则Disposable等同于Subscription。
有关RxJava2中更改的更多信息,请参见此处 。
On the Disposable instance we can unsubscribe from the event by calling d.dispose();
.
The Disposable instance holds a reference to the view. So if this code is defined outside the context of the Activity, you must unsubscribe from the event to prevent memory leaks.
在Disposable实例上,我们可以通过调用d.dispose();
取消订阅该事件d.dispose();
。
Disposable实例包含对该视图的引用。 因此,如果此代码是在Activity上下文之外定义的,则必须取消订阅该事件,以防止内存泄漏。
RxView.click()
returns an Observable. So we can add RxJava operators to perform transformations and chain implementations on it.
RxView.click()
返回一个Observable。 因此,我们可以添加RxJava运算符以在其上执行转换和链式实现。
EditText TextChanges (EditText TextChanges)
Typically to add text change listeners on an EditText we need to implement the TextWatcher methods:
通常,要在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) {
}
});
Using RxBinding we can do:
使用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
}
});
We pass the EditText inside RxTextView.textChanges
.
我们在RxTextView.textChanges
内部传递EditText。
Now let’s see how RxJava operators and transformations give us leverage.
现在,让我们看看RxJava运算符和转换如何给我们带来影响。
使用地图运算符 (Using Map operator)
Using map operator we can change the data that is being sent.
For example, in the EditText we can change the CharSequence to a String
使用地图运算符,我们可以更改正在发送的数据。
例如,在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
}
});
使用反跳运算符 (Using debounce operator)
Using a debounce operator we can delay the event action.
使用反跳运算符,我们可以延迟事件动作。
For example, on the Button click, we can set a debounce of 2 seconds. This will run the operation after 2 seconds:
例如,在“按钮”上单击,我们可以将去抖设置为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();
}
});
A debounce operation doesn’t run on the UI thread. It runs on the computational thread.
Hence you must call the main thread in the observeOn
method after that as done above.
反跳操作不在UI线程上运行。 它在计算线程上运行。
因此,如上所述,您必须在此之后在observeOn
方法中调用主线程。
A debounce operator is commonly used in EditText, especially in a SearchView to let the user stop typing for a few seconds before running the action/requests.
反跳运算符通常在EditText中使用,尤其是在SearchView中,可以使用户在运行操作/请求之前停止键入几秒钟。
使用节流阀 (Using throttleFirst)
Unlike debounce which delays the action, throttleFirst
operator is used in preventing repeated actions within a certain time interval.
ThrottleFirst is useful when it comes to preventing double actions when a Button is repeatedly clicked.
与防抖会延迟动作不同, 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();
}
});
In the above code, the Button won’t show a Toast again until after 2 seconds.
在上面的代码中,Button直到2秒后才会再次显示Toast。
Merging Multiple Button click actions
合并多个按钮单击动作
We can merge RxView.click()
Observables in the following way:
我们可以通过以下方式合并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
}
});
Multiple Click Listeners on a Button
一个按钮上的多个单击侦听器
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);
Using the share operator on the RxView.click()
observable, both the Disposables created below are run when the button is clicked.
使用可观察到的RxView.click()
上的share运算符,单击该按钮时,将同时运行下面创建的两个Disposable。
Let’s merge the above concepts in our Android Application with a few more operators:
让我们在Android应用程序中将以上概念与更多运算符合并:
项目结构 (Project Structure)
码 (Code)
activity_main.xml
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
MainActivity.java
package com.journaldev.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);
}
}
In the EditText, we’ve set a filter
operator which doesn’t set the input text onto the TextView until the length crosses a threshold.
在EditText中,我们设置了一个filter
运算符,直到长度超过阈值时才将输入文本设置到TextView上。
To clear all the Disposables, instead of calling disposable() on each of them separately, we can do the following as well:
要清除所有Disposable,而不是分别对每个Disposable()进行调用,我们还可以执行以下操作:
CompositeDisposable clearAllDisposables = new CompositeDisposable();
clearAllDisposables.add(d1);
clearAllDisposables.add(d2);
clearAllDisposables.add(d);
clearAllDisposables.clear();
The output of the application in action is given below:
实际应用程序的输出如下:
Notice how many times the FloatingActionButton was clicked. But the Toast was displayed just once.
注意单击FloatingActionButton的次数。 但是吐司只被展示了一次。
This brings an end to this tutorial. You can download the project from the link below:
本教程到此结束。 您可以从下面的链接下载项目: