博客主頁html
RxBinding 是 Jake Wharton 大神寫的框架,它的 API 可以把 Android 平臺和兼容包內的 UI 控件變爲 Observable 對象,這樣就能夠把 UI 控件的事件看成 RxJava 中的數據流來使用了。java
好比 View 的 onClick 事件,使用 RxView.clicks(view)便可獲取一個 Observable 對象,每當用戶單擊這個 View 的時候,該 Observable 對象就會發射一個事件, Observable 的觀察者就能夠經過 onNext 回調知道用戶單擊了 View。android
RxBinding GitHub 地址:https://github.com/JakeWharto...git
RxBinding 的優勢:github
Rx.Binding 的下載:segmentfault
Platform bindings:網絡
implementation 'com.jakewharton.rxbinding3:rxbinding:3.1.0'
AndroidX library bindings:app
implementation 'com.jakewharton.rxbinding3:rxbinding-core:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-appcompat:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-drawerlayout:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-leanback:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-recyclerview:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-slidingpanelayout:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-swiperefreshlayout:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager:3.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-viewpager2:3.1.0'
Google 'material' library bindings:框架
implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.1.0'
對 UI 事件(例如點擊、滑動和文本輸入)的響應幾乎是 Android App 開發的基本部分,可是 Android SDK 對 UI 事件的處理有些複雜 ,咱們一般須要使用各類 listeners、handlers、
TextWatchers 和其餘組件等組合來響應 UI 事件。這些組件中的每個都須要編寫大量的樣板代碼,更爲糟糕的是,實現這些不一樣組件的方式並不一致。例如,你能夠經過實現 OnClickListener 來處理 OnClick 件。maven
這種一致性的缺少可能會爲代碼增長不少複雜性。若是有些 UI 組件須要依賴於其餘 UI 組件的輸出,那麼事情會變得更加複雜。
即便是一個簡單的需求,例如要求用戶將其名稱輸入到 EditText ,以便個性化地展現 TextView 的文本內容,而 TextView 須要嵌套回調,這是很是難以實現和維護的(有人將嵌套回調稱爲「回調地獄」〉。
顯然,處理 UI 事件的標準化方法有大大簡化代碼的空間,而 RxBinding 就是這樣的庫,它
提供的綁定可以將任何 Android View 事件轉換爲 Observable。
一旦將 View 事件轉換爲 Observable ,它將發射數據流形式的 UI 事件,咱們就能夠訂閱這個數據流,這與訂閱其餘 Observable 方式相同。接下來,看看如何實現 OnClick 事件:
Button button = findViewById(R.id.button); RxView.clicks(button) .subscribe(new Consumer<Object>() { @Override public void accept(Object obj) throws Exception { Log.d(TAG, "RxBinding.click"); } });
這種方法不只更簡潔,並且是一種標準的實現方式,咱們能夠將其應用於整個 App 的全部 UI 事件。例如,捕獲文本輸入與捕獲點擊事件的模式是同樣的:
EditText editText = findViewById(R.id.edit_text); RxTextView.textChanges(editText) .subscribe(new Consumer<CharSequence>() { @Override public void accept(CharSequence charSequence) throws Exception { Log.d(TAG, "RxBinding.textChanges-> " + charSequence); } });
RxBinding 能夠應用於整個 App 的全部 UI 事件,下面列舉一些 RxBinding 比較常見的使用場景。
按鈕的點擊事件是每 App 都會用到的場景,可使用 RxView 的 clicks(@NonNull View view)方法來綁定 UI 控件
RxView.clicks(button1) .subscribe(new Consumer<Object>() { @Override public void accept(Object o) throws Exception { Log.d(TAG, "演示點擊事件"); } });
長點擊事件也是一個比較常見的事件,可使用 RxView 的 longClicks(@NonNull View view) 方法來綁定 UI 控件
RxView.longClicks(button2) .subscribe(new Consumer<Unit>() { @Override public void accept(Unit unit) throws Exception { Log.d(TAG, "演示長點擊事件"); } });
在弱網絡環境下,常常會遇到點擊某個按鈕沒有響應的狀況,此時心急的用戶可能會屢次點擊按鈕,從而形成事件的屢次觸發,顯然這是咱們不肯意看到的狀況。能夠利用 throttleFirst 操做符獲取某段時間內的第一次點擊事件。
RxView.clicks(button3) .compose(RxUtils.useRxViewTransformer(RxBindingAct.this)) .subscribe(new Consumer() { @Override public void accept(Object o) throws Exception { Log.d(TAG, "防止重複點擊"); } });
這裏使用了 compose 操做符,它封裝了兩個操做: 一個是防止 UI 事件的重複點擊,另外一個
是綁定 Activity 的生命週期,防止內存泄露。來看一下 useRxViewTransformer 方法的具體代碼,
它裏面包含了兩個 compose()
// 對 RxView 綁定事件 // 封裝了防止重複點擊和RxLifecycle的生命週期 public static ObservableTransformer useRxViewTransformer(final RxAppCompatActivity targetActivity) { return new ObservableTransformer() { @Override public ObservableSource apply(Observable upstream) { return upstream.compose(RxJavaUtils.preventDuplicateClicksTransformer()) .compose(targetActivity.bindToLifecycle()); } }; }
先來看看第一個 compose 的做用
// 防止重複點擊的Transformer @JvmStatic fun <T> preventDuplicateClicksTransformer(): ObservableTransformer<T, T> { return ObservableTransformer { upstream -> upstream.throttleFirst(1000, TimeUnit.MILLISECONDS) } }
它的用途是在 ls 內只取第一次點擊,這樣能夠防止 ls 內產生屢次點擊事件。
https://github.com/fengzhizi7...
App 內常見的表單驗證是用戶登陸頁面,咱們須要對用戶名、密碼作一些校驗。對於校驗,有些是服務端作的,例如,用戶名是否存在、用戶名的密碼是否正確等。而有些校驗則須要客戶端來作,例如,用戶名是否輸入、輸入的用戶名是否規範、密碼是否輸入等。
例如,手機號碼不足 11 位時,會出現一個提示
若是密碼沒有輸入,就點擊「登陸」按鈕,則會彈出一個提示
EditText phone = findViewById(R.id.phone); EditText password = findViewById(R.id.password); Observable<CharSequence> observablePhone = RxTextView.textChanges(phone); Observable<CharSequence> observablePassword = RxTextView.textChanges(password); Observable.combineLatest(observablePhone, observablePassword, new BiFunction<CharSequence, CharSequence, ValidationResult>() { @Override public ValidationResult apply(CharSequence charSequence1, CharSequence charSequence2) throws Exception { ValidationResult result = new ValidationResult(); if (charSequence1.length() == 0) { result.flag = false; result.message = "手機號碼不能爲空"; } else if (charSequence1.length() != 11) { result.flag = false; result.message = "手機號碼須要11位"; } else if (charSequence2.length() == 0) { result.flag = false; result.message = "密碼不能爲空"; } return result; } }).subscribe(new Consumer<ValidationResult>() { @Override public void accept(ValidationResult validationResult) throws Exception { result = validationResult; } }); Button login = findViewById(R.id.login); RxView.clicks(login) .compose(RxUtils.useRxViewTransformer(RxBindingAct.this)) .subscribeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<Object>() { @Override public void accept(Object o) throws Exception { if (result == null) return; if (result.flag) { Log.d(TAG, "result-> 登錄成功."); } else { Log.d(TAG, "result-> " + result.message); } } });
combineLatest 的做用是將多個 Observable 發射的數據組裝起來而後再發射出來。這裏兩個輸入框只要內容發生變化,就會發送 Observable ,此時咱們便可在 BiFunction 中利用驗證方法去判斷輸入框中最新的內容,最終返 ValidationResult 對象。
用戶註冊帳號時,通常須要獲取驗證碼來驗證手機號碼
在等待驗證碼的過程當中, App 界面上一般會有一個倒計時,提示咱們剩餘 xx 秒能夠從新獲取驗證碼。
final long MAX_COUNT_TIME = 60; RxView.clicks(button5) .throttleFirst(MAX_COUNT_TIME, TimeUnit.SECONDS) .flatMap(new Function<Object, ObservableSource<Long>>() { @Override public ObservableSource<Long> apply(Object o) throws Exception { // 1. 更新發送按鈕的狀態,並初始化顯現倒計時文字 Log.d(TAG, "flatMap"); // 2. 返回 n 秒內的倒計時觀察者對象 return Observable.interval(1, TimeUnit.SECONDS, Schedulers.io()).take(MAX_COUNT_TIME); } }) // 將遞增數字替換成遞減的倒計時數字 .map(new Function<Long, Long>() { @Override public Long apply(Long aLong) throws Exception { Log.d(TAG, "map: " + aLong); return MAX_COUNT_TIME - (aLong - 1); } }) // 切換到 Android 的主線程 .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<Long>() { @Override public void accept(Long aLong) throws Exception { if (aLong == 0) { Log.d(TAG, "倒計時完成."); } else { Log.d(TAG, "倒計時剩餘:" + aLong + " 秒"); } } });
Observable.interval(1, TimeUnit.SECONDS, Schedulers.io()) 表示每 ls 發射一次數據。
take(MAX_COUNT_TIME) 和後面的操做表示按鈕在 60s 內不可再次被點擊,而且在這段時間每隔一秒發射一次數據用於更新 UI。 在實際使用中,須要在 flatMap 裏作獲取短信驗證碼絡請求。
Rx.Bindin 提供了一個 rxbinding-recyclerview 的庫,專門用於對 RecyclerView 支持。
其中, RxRecyclerView 提供了幾個狀態的觀察:
RxRecyclerView .scrollStateChanges(recyclerView) .subscribe(new Consumer<Integer>() { @Override public void accept(Integer scrollState) throws Exception { Log.d(TAG, "scrollState: " + scrollState); } });
scrollState 表示 RecyclerView 中定義的滾動狀態。
// RecyclerView 當前沒有滾動 public static final int SCROLL_STATE_IDLE = 0; // RecyclerView 正在被拖動 public static final int SCROLL_STATE_DRAGGING = 1; // 手指已經離開屏幕,RecyclerView 正在作動畫移動到最終位置 public static final int SCROLL_STATE_SETTLING = 2;
除能夠對 RecyclerView 狀態的進行監聽外,還能對點擊事件進行監聽
在 Adapter 的 onBindViewHolder() 中,可使用 clicks() 來綁定 itemView 的點擊事件。
能夠利用 RxJava 的操做符,例如 publish、share、replay ,實現對 UI 控件的屢次監聽
Observable observable = RxView.clicks(button5) .compose(RxUtils.useRxViewTransformer(this)) .share(); observable.subscribe(new Consumer() { @Override public void accept(Object o) throws Exception { Log.d(TAG, "第一次監聽"); } }); observable.subscribe(new Consumer() { @Override public void accept(Object o) throws Exception { Log.d(TAG, "第二次監聽"); } }); // 執行結果 第一次監聽 第二次監聽
使用了 share 操做符,隨後作了兩次監監聽,點擊該控件打印兩次
https://developer.android.goo...
https://developer.android.goo...
Android 6.0 帶來一個很大變化就是權限機制的改變,特別是運行時權限。
Android 6.0+添加的運行時權限可分爲兩類:
Dangerous Permissions 是有分組的。App 運行在 Android 6.0+的手機之上,若是用戶申請了某個 Dangerous Permissions ,而該用戶己經受權了一個與他如今申請的是同一組的 Dangerous Permissions ,那麼系統會自動受權,無須用戶再次受權。
對於 Android 6.0 如下的手機,用戶在安裝 App 的時候能夠看到權限聲明產生一個權限列表,用戶只有在贊成以後才能完成 App 的安裝。若是用戶想要使用某個 App,就須要忍受其一些沒必要要的權限(例如訪問通信錄、短信的權限等)。從 Android 6.0 之後咱們能夠直接安裝 App,當 App 須要咱們授予不恰當的權限的時候,咱們能夠予以拒絕。固然做爲用戶也能夠在手機的設置界面裏對每一個 App 的權限進行查看,井對單個權限進行受權或者解除受權。
App 的 targetSdkVersion 是 23 及以上,而且 App 運行在 Android 6.0 及以上的設備時,需同時知足這兩個條件才須要動態地請求危險權限。
在處理運行時權限時,一般須要兩步:
RxPermissions 的出現能夠簡化這些步驟,它是基於 RxJava 開發的 Android 框架,幫助 Android 6.0 以後處理運行時權限的檢測。
RxPermissions GitHub 地址:https://github.com/tbruyelle/...
RxPermissions 的下載:
allprojects { repositories { ... maven { url 'https://jitpack.io' } } } dependencies { implementation 'com.github.tbruyelle:rxpermissions:0.10.2' }
在 RxPermissions 使用以前,須要先建立 RxPermissions 的實例。能夠在 Activity 的 onCreate() 中進行建立,建立以後才能使用它。
RxPermissions rxPermissions = new RxPermissions(this);
舉一個撥打電話的例子,CALL_PHONE 在 Android 6.0 以後是一個 Dangerous Permissions, 第一次使用時須要動態申請該權限,只有獲得容許才能完成後面打電話的動做。
RxView.clicks(findViewById(R.id.button)) .subscribe(new Consumer<Object>() { @Override public void accept(Object object) throws Exception { rxPermissions.request(Manifest.permission.CALL_PHONE) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { if (granted) { Log.d(TAG, "受權成功."); } else { Log.d(TAG, "受權失敗"); } } }); } });
RxBinding 能夠結合 compose 操做符來使用 RxPermissions 。
RxView.clicks(findViewById(R.id.button)) .compose(rxPermissions.<Unit>ensure(Manifest.permission.CALL_PHONE)) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { if (granted) { Log.d(TAG, "受權成功."); } else { Log.d(TAG, "受權失敗"); } } });
RxPermissions 也支持申請多個權限,如同時申請 CAMERA 和 WRITE_EXTERNAL_STORAGE 的權限。單擊按鈕以後,須要受權兩次,任何一次受權的失敗都會致使「打開相機失敗」。只有兩次申請權限都成功,才能「打開相機成功」。
RxView.clicks(findViewById(R.id.button)) .compose(rxPermissions.ensure(Manifest.permission.CALL_PHONE, Manifest.permission.WRITE_EXTERNAL_STORAGE)) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { if (granted) { Log.d(TAG, "打開相機成功."); } else { Log.d(TAG, "打開相機失敗"); } } });
trello 的 RxLifecycle
https://github.com/trello/RxLifecycle
知乎的 RxLifecycle
https://github.com/zhihu/RxLifecycle
其它的 RxLifecycle,這個與知乎的 RxLifecycle 的區別是, LifecycleTransformer 實現了多個 Transformer 接口。
https://github.com/fengzhizi715/SAF/tree/master/saf-rxlifecycle
若是個人文章對您有幫助,不妨點個贊鼓勵一下(^_^)