序文
RxJavaを使って以来、Rxシリーズのレスポンシブプログラミングにすっかりハマってしまったので、今回はRxパッケージの一つであるRxBindingについて学んでいきましょう。RxBinding とは何ですか? これは、マスター Jake Wharton によるオープン ソース ライブラリのセットであり、Android のさまざまな UI コントロールのアクション イベントを RxJava のデータ ストリームに変換できます。つまり、RxBinding を使用すると、UI イベントを RxJava の形式で処理できます。この記事では、主に RxView の関連する View イベントをバインドする方法について説明します。RxBinding には、主に RxView、RxTextView、RxAdapterView、RxCompoundButton などが含まれます。まとめて書くと長くなるので、この記事では主にRxViewについて説明します。
RxView
RxBinding は、Android のさまざまな UI コントロールのアクション イベントを RxJava のデータ ストリームに変換できるオープン ソース ライブラリのセットです。RxBinding を使用して、RxJava オブザーバー パターンで UI イベントを処理します。RxView は RxBinding のコンポーネントです
依存関係を導入する
注: RxBinding パッケージには RxJava コンテンツが含まれているため、RxJava 依存関係を追加する必要はありません。
implementation 'com.jakewharton.rxbinding2:rxbinding:2.2.0'
コード分析
RxView.clicks(ビュービュー)
Through the source code, it can be found that it encapsulates the View.OnClickListener click listener, and calls the click method to return an observable object. このビューがクリックされるたびに、オブザーバブル オブジェクトはイベントを発行し、onNext(対応するオブザーバーは onNext() コールバックを通じてクリック イベントに応答できます. RxBinding を使用すると、クリックの手ぶれ防止効果も得られます. コード:
RxView.clicks(button)
.throttleFirst(2, TimeUnit.SECONDS)
.compose(
RxPermissions(this@MainActivity)
.ensure(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
)
).subscribe {
Toast.makeText(this@MainActivity, "你好!", Toast.LENGTH_SHORT).show()
}
補充:
compose:组成
ensure():类似request()方法
subscribe:订阅
throttleFirst(long windowDuration, TimeUnit unit) は、一定時間内の最初 (throttleFirst) または最後の (throttleLast) クリック イベントにのみ応答するように設定されています。windowDuration はアンチシェイクで、unit は時間単位です。このメソッドを呼び出すと、短い時間でのクリックの繰り返しを防ぐことができます. この例では、手ぶれ防止時間を 2 秒に設定しています
RxJava RxBinding RxView コントロール イベント MD ユース ケース
public class RxBindingActivity extends RxFragmentActivity {
private static final String[] ARRAY = {"包青天", "白乾涛", "baiqiantao", "bqt", "RxBinding", "RxView", "1", "2", "3", "4", "5",};
private ImageView iv1, iv2, iv3, iv4, iv5;
private EditText et1, et2, et3;
private Button btn;
private CheckBox cb;
private ListView listView;
private int type;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rxbinding);
iv1 = findViewById(R.id.iv1);
iv2 = findViewById(R.id.iv2);
iv3 = findViewById(R.id.iv3);
iv4 = findViewById(R.id.iv4);
iv5 = findViewById(R.id.iv5);
btn = findViewById(R.id.btn);
et1 = findViewById(R.id.et1);
et2 = findViewById(R.id.et2);
et3 = findViewById(R.id.et3);
cb = findViewById(R.id.cb);
listView = findViewById(R.id.lv);
listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Arrays.asList(ARRAY)));
type = getIntent().getIntExtra("type", 0);
log("type=" + type);
switch (type) {
case 0:
event();
break;
case 1:
accept();
break;
case 2:
usefulDemo();
break;
}
}
@Override
protected void onResume() {
super.onResume();
if (type == 3) {
bufferDemo();
}
}
@SuppressLint("NewApi")
private void event() {
RxView.attaches(iv1).subscribe(o -> log("attach event"));
RxView.attachEvents(iv1).subscribe(viewAttachEvent -> log("attach event"));
RxView.clicks(iv1).subscribe(o -> log("click event")); //setOnClickListener
RxView.detaches(iv1).subscribe(o -> log("detach event"));
RxView.drags(listView).subscribe(dragEvent -> log("drag event:" + dragEvent.getAction())); //setOnDragListener
RxView.draws(listView).subscribe(o -> log("draw event")); // ViewTreeObserver#addOnDrawListener
RxView.focusChanges(iv1).subscribe(hasFocus -> log("focus change event:" + hasFocus)); //setOnFocusChangeListener
RxView.globalLayouts(listView).subscribe(o -> log("global event")); //ViewTreeObserver#addOnGlobalLayoutListener
RxView.hovers(iv1).subscribe(motionEvent -> log("hover event:" + motionEvent.getAction())); //setOnHoverListene,悬停事件
RxView.keys(iv1).subscribe(keyEvent -> log("key event:" + keyEvent.getAction())); //setOnKeyListener
RxView.layoutChanges(listView).subscribe(o -> log("layout event")); //addOnLayoutChangeListener
RxView.layoutChangeEvents(listView).subscribe(viewLayoutChangeEvent -> log("layout event"));//addOnLayoutChangeListener
RxView.longClicks(iv1).subscribe(o -> log("long click event")); //setOnLongClickListener
RxView.scrollChangeEvents(listView).subscribe(viewScrollChangeEvent -> log("scroll event")); //setOnScrollChangeListener
RxView.systemUiVisibilityChanges(listView).subscribe(vii -> log("visible event" + vii)); //setOnSystemUiVisibilityChangeListener
RxView.touches(listView).subscribe(motionEvent -> { //setOnTouchListener
log("touch event:" + motionEvent.getAction());
listView.onTouchEvent(motionEvent);//如果不传给listView,则listView将不能获取到Touch事件,那么listView会出现不能滑动等问题
});
RxAdapterView.itemClicks(listView).subscribe(position -> log("item click event:" + position)); //setOnItemClickListener
RxAdapterView.itemClickEvents(listView).subscribe(adapterViewItemClickEvent -> log("item click event")); //点击
RxAdapterView.itemLongClicks(listView).subscribe(position -> log("item long click event:" + position)); //setOnItemLongClickListener
RxAdapterView.itemLongClickEvents(listView).subscribe(adapterViewItemLongClickEvent -> log("item long click event")); //长点击
RxAdapterView.itemSelections(listView).subscribe(position -> log("item select event:" + position)); //setOnItemSelectedListener
RxAdapterView.selectionEvents(listView).subscribe(adapterViewSelectionEvent -> log("item select event")); //选择
RxAdapter.dataChanges(listView.getAdapter()).subscribe(listAdapter -> log("data change event"));//registerDataSetObserver
RxAbsListView.scrollEvents(listView).subscribe(absListViewScrollEvent -> log("scroll event")); //setOnScrollListener
RxViewGroup.changeEvents(listView).subscribe(viewGroupHierarchyChangeEvent -> log("哈"));//setOnHierarchyChangeListener
RxCompoundButton.checkedChanges(cb).subscribe(isChecked -> log("check event:" + isChecked)); //setOnCheckedChangeListener
RxTextView.textChanges(et1).subscribe(cs -> log("text change event:" + cs.toString())); //addTextChangedListener
RxTextView.textChangeEvents(et2).subscribe(textViewTextChangeEvent -> log("text change event")); //addTextChangedListener
RxTextView.afterTextChangeEvents(et3).subscribe(textViewAfterTextChangeEvent -> log("aftet text change event"));
RxTextView.beforeTextChangeEvents(et3).subscribe(textViewBeforeTextChangeEvent -> log("before text change event"));
RxTextView.editorActions(et1).subscribe(actionId -> log("editor event:" + actionId)); //setOnEditorActionListener,需要设置inputType
RxTextView.editorActionEvents(et2).subscribe(textViewEditorActionEvent -> log("editor event")); //点击软键盘上的回车键
}
private void accept() {
try {
RxView.visibility(iv1).accept(false);//view.setVisibility(value ? View.VISIBLE : View.GONE)
RxView.visibility(iv2, View.GONE).accept(true);//view.setVisibility(value ? View.VISIBLE : visibilityWhenFalse)
RxView.visibility(iv3, View.GONE).accept(false);
RxView.visibility(iv4, View.INVISIBLE).accept(false);
RxView.visibility(iv5, View.INVISIBLE).accept(true);
RxAdapterView.selection(listView).accept(1); //view.setSelection(position)
RxCompoundButton.checked(cb).accept(true); //view.setChecked(value)
RxCompoundButton.toggle(cb).accept(null); //view.toggle(),切换状态
RxTextView.color(et1).accept(Color.RED); //view.setTextColor(color)
RxTextView.textRes(et1).accept(R.string.app_name); //view.setText(int resId)
RxTextView.hintRes(et2).accept(R.string.app_name); //view.setHint(int resId)
RxTextView.errorRes(et3).accept(R.string.app_name); //view.setError(int resId)
RxTextView.text(et1).accept("text"); //view.setText(String text)
RxTextView.hint(et2).accept("hint"); //view.setHint(String text)
RxTextView.error(et3).accept("error"); //view.setError(String text)
} catch (Exception e) {
e.printStackTrace();
}
}
private void usefulDemo() {
RxView.clicks(iv1)
.throttleFirst(2, TimeUnit.SECONDS) //throttleFirst只响应第一次,throttleLast只响应最后一次
.subscribe(o -> log("防抖动"));
RxView.clicks(iv2)
.compose(new RxPermissions(this).ensure(Manifest.permission.CAMERA)) //动态获取权限
.subscribe(granted -> log(granted ? "已赋予权限" : "已拒绝权限"));
RxTextView.textChanges(et1)
.debounce(500, TimeUnit.MILLISECONDS) //防抖动,控件操作时间间隔,去除发送频率过快的项
.subscribe(charSequence -> log(charSequence.toString()));
Observable<CharSequence> observable1 = RxTextView.textChanges(et2).skip(1);
Observable<CharSequence> observable2 = RxTextView.textChanges(et3).skip(1);
Observable.combineLatest(observable1, observable2, //合并监听、表单验证
(phone, password) -> {
log(phone + "_" + password);
return phone.toString().startsWith("1") && password.toString().endsWith("1");
}).subscribe(isValid -> btn.setEnabled(isValid));
RxView.clicks(btn) //发送验证码功能
.doOnNext(o -> btn.setEnabled(false))
.subscribe(o -> Observable.interval(1, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.take(10)
.compose(bindToLifecycle())
.map(aLong -> 10 - aLong + " 秒后重新获取")
.subscribe(string -> btn.setText(string),
Throwable::printStackTrace,
() -> {
btn.setEnabled(true);
btn.setText("重新获取");
}));
}
private void bufferDemo() {
RxView.clicks(iv1)
.buffer(1000, TimeUnit.MILLISECONDS, 5) //效果仅仅是,每隔1秒钟收集一下此1秒钟内的点击次数
.compose(bindUntilEvent(ActivityEvent.STOP))//在 onStop 时取消
.subscribe(list -> Log.i("【bqt】", "iv1一秒钟内的点击次数:" + list.size()));
RxView.clicks(iv2)
.map(obj -> 1) //和上面的情况基本一致
.buffer(1000, TimeUnit.MILLISECONDS)
.compose(bindUntilEvent(ActivityEvent.STOP))
.subscribe(list -> Log.i("【bqt】", "iv2一秒钟内的点击次数:" + list.size()));
//这种效果可能不是你想要的效果,你想要的效果可能是:在1秒钟内点击次数为多少次就是几次连击
Observable<Object> observable = RxView.clicks(iv3).share();
observable.buffer(observable.debounce(200, TimeUnit.MILLISECONDS).compose(bindUntilEvent(ActivityEvent.STOP)))
.subscribe(list -> Log.i("【bqt】", "iv3连续点击次数:" + list.size()));//这里的时间指的是任意两次点击最长间隔时间);
}
private void log(String s) {
Log.i("【bqt】", s);
}
}
レイアウト
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv1"
android:layout_width="50dp"
android:layout_height="match_parent"
android:src="@drawable/icon"/>
<ImageView
android:id="@+id/iv2"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
android:background="#f00"/>
<ImageView
android:id="@+id/iv3"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
android:background="#0f0"/>
<ImageView
android:id="@+id/iv4"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
android:background="#00f"/>
<ImageView
android:id="@+id/iv5"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
android:background="#000"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<EditText
android:id="@+id/et1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:hint="et1"
android:inputType="text"/>
<EditText
android:id="@+id/et2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:hint="et2"
android:imeOptions="actionDone"
android:inputType="phone"/>
<EditText
android:id="@+id/et3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:hint="et3"
android:imeOptions="actionSearch"
android:inputType="textPassword"/>
</LinearLayout>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击获取验证码"
android:textSize="12sp"/>
<CheckBox
android:id="@+id/cb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#300f"
android:checked="true"
android:text="已阅读并同意用户协议"/>
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
要約する
RxBinding の使い方は非常に簡単です.RxView には、アタッチ、デタッチ、フォーカス変更、グローバル レイアウト、ホバー、タッチなどがありますので、1 つずつ説明しませんが、自分で試すことができます。