詳細解析 RxAndroid 的使用方式

RxAndroid是RxJava的擴展, 優雅地處理異步請求. RxAndroid配合Lambda表達式, 精簡處理回調, 使程序更具備可讀性. Rx做爲Android最優秀的開源庫之一, 極大地提升生產力, 咱們須要掌握. 本文由淺入深, 介紹一些常見的使用方法, 並附有源碼.java

更多: http://www.wangchenlong.org/react

本文代碼的GitHub下載地址.android

要點包含:
(1) 鏈式表達式的使用方式.
(2) Lambda的應用.
(3) Rx處理網絡請求.
(4) 線程自動管理, 防止內存泄露.
(5) RxBinding綁定控件的異步事件.git

Rx


基礎

固然, 從一個嶄新的HelloWorld項目開始.github

添加Gradle配置.json

1
2
3
compile 'com.jakewharton:butterknife:7.0.1'
compile 'io.reactivex:rxandroid:1.1.0' // RxAndroid
compile 'io.reactivex:rxjava:1.1.0' // 推薦同時加載RxJava

 

RxAndroid是本文的核心依賴, 同時添加RxJava. 還有ButterKnife註解庫.api

Lambda表達式, 是寫出優雅代碼的關鍵, 參考.數組

1
2
3
4
5
6
7
8
9
10
11
12
plugins {
id "me.tatarka.retrolambda" version "3.2.4"
}

android {
...

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

 

Gradle 2.1+以上, 配置很是簡單, 添加一個plugin和一個Java1.8兼容便可.安全

從主MainActivity跳轉至SimpleActivity.網絡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 主Activity, 用於跳轉各個模塊.
*
* @author wangchenlong
*/
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

// 跳轉簡單的頁面
public void gotoSimpleModule(View view) {
startActivity(new Intent(this, SimpleActivity.class));
}

// 跳轉複雜的頁面
public void gotoMoreModule(View view) {
startActivity(new Intent(this, MoreActivity.class));
}

// 跳轉Lambda的頁面
public void gotoLambdaModule(View view) {
startActivity(new Intent(this, LambdaActivity.class));
}

// 跳轉網絡的頁面
public void gotoNetworkModule(View view) {
startActivity(new Intent(this, NetworkActivity.class));
}

// 跳轉線程安全的頁面
public void gotoSafeModule(View view) {
startActivity(new Intent(this, SafeActivity.class));
}
}

 

SimpleActivity中, 建立一個觀察者, 收到字符串的返回.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    // 觀察事件發生
Observable.OnSubscribe mObservableAction = new Observable.OnSubscribe<String>() {
@Override public void call(Subscriber<? super String> subscriber) {
subscriber.onNext(sayMyName()); // 發送事件
subscriber.onCompleted(); // 完成事件
}
};

...

// 建立字符串
private String sayMyName() {
return "Hello, I am your friend, Spike!";
}

 

建立兩個訂閱者, 使用字符串輸出信息.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 訂閱者, 接收字符串, 修改控件
Subscriber<String> mTextSubscriber = new Subscriber<String>() {
@Override public void onCompleted() {

}

@Override public void onError(Throwable e) {

}

@Override public void onNext(String s) {
mTvText.setText(s); // 設置文字
}
};

// 訂閱者, 接收字符串, 提示信息
Subscriber<String> mToastSubscriber = new Subscriber<String>() {
@Override public void onCompleted() {

}

@Override public void onError(Throwable e) {

}

@Override public void onNext(String s) {
Toast.makeText(SimpleActivity.this, s, Toast.LENGTH_SHORT).show();
}
};

 

在頁面中, 觀察者接收信息, 發送至主線程AndroidSchedulers.mainThread(), 再傳遞給訂閱者, 由訂閱者最終處理消息. 接收信息能夠是同步, 也能夠是異步.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
ButterKnife.bind(this);

// 註冊觀察活動
@SuppressWarnings("unchecked")
Observable<String> observable = Observable.create(mObservableAction);

// 分發訂閱信息
observable.observeOn(AndroidSchedulers.mainThread());
observable.subscribe(mTextSubscriber);
observable.subscribe(mToastSubscriber);
}

最基礎的RxAndroid使用.
基礎


更多

咱們已經熟悉了初步的使用方式, 在接着學習一些其餘方法, 如

just: 獲取輸入數據, 直接分發, 更加簡潔, 省略其餘回調.
from: 獲取輸入數組, 轉變單個元素分發.
map: 映射, 對輸入數據進行轉換, 如大寫.
flatMap: 增大, 本意就是增肥, 把輸入數組映射多個值, 依次分發.
reduce: 簡化, 正好相反, 把多個數組的值, 組合成一個數據.

來看看這個示例, 設置兩個不一樣類型數組, 做爲輸入源, 根據不一樣狀況分發數據.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
* 更多的RxAndroid的使用方法.
* <p>
* Created by wangchenlong on 15/12/30.
*/
public class MoreActivity extends Activity {

@Bind(R.id.simple_tv_text) TextView mTvText;

final String[] mManyWords = {"Hello", "I", "am", "your", "friend", "Spike"};
final List<String> mManyWordList = Arrays.asList(mManyWords);

// Action相似訂閱者, 設置TextView
private Action1<String> mTextViewAction = new Action1<String>() {
@Override public void call(String s) {
mTvText.setText(s);
}
};

// Action設置Toast
private Action1<String> mToastAction = new Action1<String>() {
@Override public void call(String s) {
Toast.makeText(MoreActivity.this, s, Toast.LENGTH_SHORT).show();
}
};

// 設置映射函數
private Func1<List<String>, Observable<String>> mOneLetterFunc = new Func1<List<String>, Observable<String>>() {
@Override public Observable<String> call(List<String> strings) {
return Observable.from(strings); // 映射字符串
}
};

// 設置大寫字母
private Func1<String, String> mUpperLetterFunc = new Func1<String, String>() {
@Override public String call(String s) {
return s.toUpperCase(); // 大小字母
}
};

// 鏈接字符串
private Func2<String, String, String> mMergeStringFunc = new Func2<String, String, String>() {
@Override public String call(String s, String s2) {
return String.format("%s %s", s, s2); // 空格鏈接字符串
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
ButterKnife.bind(this);

// 添加字符串, 省略Action的其餘方法, 只使用一個onNext.
Observable<String> obShow = Observable.just(sayMyName());

// 先映射, 再設置TextView
obShow.observeOn(AndroidSchedulers.mainThread())
.map(mUpperLetterFunc).subscribe(mTextViewAction);

// 單獨顯示數組中的每一個元素
Observable<String> obMap = Observable.from(mManyWords);

// 映射以後分發
obMap.observeOn(AndroidSchedulers.mainThread())
.map(mUpperLetterFunc).subscribe(mToastAction);

// 優化過的代碼, 直接獲取數組, 再分發, 再合併, 再顯示toast, Toast順次執行.
Observable.just(mManyWordList)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(mOneLetterFunc)
.reduce(mMergeStringFunc)
.subscribe(mToastAction);
}

// 建立字符串
private String sayMyName() {
return "Hello, I am your friend, Spike!";
}
}

 

此次簡化調用代碼, 由於有時候咱們對異常並非很關心,
只要能catch異常便可, 所以流僅僅關注真正須要的部分.

輸入字符串, 變換大寫, 輸出至控件中顯示.

1
2
3
4
5
6
// 添加字符串, 省略Action的其餘方法, 只使用一個onNext.
Observable<String> obShow = Observable.just(sayMyName());

// 先映射, 再設置TextView
obShow.observeOn(AndroidSchedulers.mainThread())
.map(mUpperLetterFunc).subscribe(mTextViewAction);

 

just能夠很是簡單的獲取任何數據, 分發時, 選擇使用的線程.
map是對輸入數據加工, 轉換類型, 輸入Func1, 準換大寫字母.
Func1表明使用一個參數的函數, 前面是參數, 後面是返回值.
Action1表明最終動做, 於是不須要返回值, 而且一個參數.

輸入數組, 單獨分發數組中每個元素, 轉換大寫, 輸入Toast連續顯示.

1
2
3
4
5
6
// 單獨顯示數組中的每一個元素
Observable<String> obMap = Observable.from(mManyWords);

// 映射以後分發
obMap.observeOn(AndroidSchedulers.mainThread())
.map(mUpperLetterFunc).subscribe(mToastAction);

 

from是讀取數組中的值, 每次單獨分發, 並分發屢次, 其他相似.

輸入數組, 映射爲單獨分發, 並組合到一塊兒, 集中顯示.

1
2
3
4
5
6
// 優化過的代碼, 直接獲取數組, 再分發, 再合併, 再顯示toast, Toast順次執行.
Observable.just(mManyWordList)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(mOneLetterFunc)
.reduce(mMergeStringFunc)
.subscribe(mToastAction);

 

此次是使用just分發數組, 則分發數據就是數組, 並非數組中的元素.
flatMap把數組轉換爲單獨分發, Func1內部使用from拆分數組.
reduce把單獨分發數據集中到一塊兒, 再統一分發, 使用Func2.
最終使用Action1顯示得到數據. 本次代碼也更加簡潔.

由此咱們能夠觀察到, Rx的寫法能夠是多種多樣, 合理的寫法會更加優雅.

效果
效果


Lambda

Lambda表達式和Rx很是契合, 能夠省略大量的內部類, 如Func和Action.
咱們把上個示例, 用Lambda再寫一次, 功能相同.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* Lambda表達式寫法
* <p>
* Created by wangchenlong on 15/12/31.
*/
public class LambdaActivity extends Activity {

@Bind(R.id.simple_tv_text) TextView mTvText;

final String[] mManyWords = {"Hello", "I", "am", "your", "friend", "Spike"};
final List<String> mManyWordList = Arrays.asList(mManyWords);

@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
ButterKnife.bind(this);

// 添加字符串, 省略Action的其餘方法, 只使用一個onNext.
Observable<String> obShow = Observable.just(sayMyName());

// 先映射, 再設置TextView
obShow.observeOn(AndroidSchedulers.mainThread())
.map(String::toUpperCase).subscribe(mTvText::setText);

// 單獨顯示數組中的每一個元素
Observable<String> obMap = Observable.from(mManyWords);

// 映射以後分發
obMap.observeOn(AndroidSchedulers.mainThread())
.map(String::toUpperCase)
.subscribe(this::showToast);

// 優化過的代碼, 直接獲取數組, 再分發, 再合併, 再顯示toast, Toast順次執行.
Observable.just(mManyWordList)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(Observable::from)
.reduce(this::mergeString)
.subscribe(this::showToast);
}

// 建立字符串
private String sayMyName() {
return "Hello, I am your friend, Spike!";
}

// 顯示Toast
private void showToast(String s) {
Toast.makeText(LambdaActivity.this, s, Toast.LENGTH_SHORT).show();
}

// 合併字符串
private String mergeString(String s1, String s2) {
return String.format("%s %s", s1, s2);
}
}

此次沒有使用常規的Lambda表達式, 而是更簡單的方法引用(Method References).
方法引用: 方法參數和返回值與Lambda表達式相同時, 使用方法名代替.


網絡請求

Retrofit是網絡請求庫, 剛推出2.0版本. Rx的一個核心應用就是處理異步網絡請求, 結合Retrofit, 會更加方便和簡潔. 參考.

引入庫

1
2
3
4
5
6
7
compile 'com.android.support:recyclerview-v7:23.1.1' // RecyclerView

compile 'com.squareup.retrofit:retrofit:2.0.0-beta2' // Retrofit網絡處理
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2' // Retrofit的rx解析庫
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2' // Retrofit的gson庫

compile 'com.squareup.picasso:picasso:2.5.2' // Picasso網絡圖片加載

 

recyclerviewpicasso爲了顯示. retrofit系列是網絡請求.

主頁使用一個簡單的列表視圖, 展現Github的用戶信息.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Rx的網絡請求方式
* <p>
* Created by wangchenlong on 15/12/31.
*/
public class NetworkActivity extends Activity {

@Bind(R.id.network_rv_list) RecyclerView mRvList; // 列表

@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_network);
ButterKnife.bind(this);

// 設置Layout管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRvList.setLayoutManager(layoutManager);

// 設置適配器
UserListAdapter adapter = new UserListAdapter(this::gotoDetailPage);
NetworkWrapper.getUsersInto(adapter);
mRvList.setAdapter(adapter);
}

// 點擊的回調
public interface UserClickCallback {
void onItemClicked(String name);
}

// 跳轉到庫詳情頁面
private void gotoDetailPage(String name) {
startActivity(NetworkDetailActivity.from(NetworkActivity.this, name));
}
}

 

在列表中提供點擊用戶信息跳轉至用戶詳情.
NetworkWrapper.getUsersInto(adapter) 請求網絡, 設置適配器信息.

關鍵部分, 適配器, 其中包含ViewHolder類和數據類.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
* 顯示列表
* <p>
* Created by wangchenlong on 15/12/31.
*/
public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.UserViewHolder> {

private List<GitHubUser> mUsers; // 用戶名集合

private NetworkActivity.UserClickCallback mCallback; // 用戶點擊項的回調

public UserListAdapter(NetworkActivity.UserClickCallback callback) {
mUsers = new ArrayList<>();
mCallback = callback;
}

public void addUser(GitHubUser user) {
mUsers.add(user);
notifyItemInserted(mUsers.size() - 1); // 最後一位
}

@Override public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View item = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_network_user, parent, false);
return new UserViewHolder(item, mCallback);
}

@Override public void onBindViewHolder(UserViewHolder holder, int position) {
holder.bindTo(mUsers.get(position));
}

@Override public int getItemCount() {
return mUsers.size();
}

// Adapter的ViewHolder
public static class UserViewHolder extends RecyclerView.ViewHolder {

@Bind(R.id.network_item_iv_user_picture) ImageView mIvUserPicture;
@Bind(R.id.network_item_tv_user_name) TextView mTvUserName;
@Bind(R.id.network_item_tv_user_login) TextView mTvUserLogin;
@Bind(R.id.network_item_tv_user_page) TextView mTvUserPage;

public UserViewHolder(View itemView, NetworkActivity.UserClickCallback callback) {
super(itemView);
ButterKnife.bind(this, itemView);
// 綁定點擊事件
itemView.setOnClickListener(v ->
callback.onItemClicked(mTvUserLogin.getText().toString()));
}

// 綁定數據
public void bindTo(GitHubUser user) {
mTvUserName.setText(user.name);
mTvUserLogin.setText(user.login);
mTvUserPage.setText(user.repos_url);

Picasso.with(mIvUserPicture.getContext())
.load(user.avatar_url)
.placeholder(R.drawable.ic_person_black_24dp)
.into(mIvUserPicture);
}
}

// 用戶類, 名稱必須與Json解析相同
public static class GitHubUser {
public String login;
public String avatar_url;
public String name;
public String repos_url;
}
}

 

添加數據addUser, 其中notifyItemInserted通知更新.
能夠自動生成Json解析類的網站.

首先建立`Retrofit``服務, 經過服務獲取數據, 再依次分發給適配器.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 用戶獲取類
* <p>
* Created by wangchenlong on 15/12/31.
*/
public class NetworkWrapper {
private static final String[] mFamousUsers =
{"SpikeKing", "JakeWharton", "rock3r", "Takhion", "dextorer", "Mariuxtheone"};

// 獲取用戶信息
public static void getUsersInto(final UserListAdapter adapter) {
GitHubService gitHubService =
ServiceFactory.createServiceFrom(GitHubService.class, GitHubService.ENDPOINT);

Observable.from(mFamousUsers)
.flatMap(gitHubService::getUserData)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(adapter::addUser);
}

// 獲取庫信息
public static void getReposInfo(final String username, final RepoListAdapter adapter) {
GitHubService gitHubService =
ServiceFactory.createServiceFrom(GitHubService.class, GitHubService.ENDPOINT);

gitHubService.getRepoData(username)
.flatMap(Observable::from)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(adapter::addRepo);
}
}

 

網絡請求沒法在主線程上執行, 須要啓動異步線程, 如Schedulers.newThread().
使用工廠模式ServiceFactory建立服務, 也能夠單首創建服務.

建立Retrofit服務的工廠類.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 工廠模式
* <p>
* Created by wangchenlong on 15/12/31.
*/
public class ServiceFactory {
public static <T> T createServiceFrom(final Class<T> serviceClass, String endpoint) {
Retrofit adapter = new Retrofit.Builder()
.baseUrl(endpoint)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 添加Rx適配器
.addConverterFactory(GsonConverterFactory.create()) // 添加Gson轉換器
.build();
return adapter.create(serviceClass);
}
}

 

這是Retrofit 2.0的寫法, 注意須要添加Rx和Gson的解析.

設置網絡請求的Url.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* GitHub的服務
* <p>
* Created by wangchenlong on 15/12/31.
*/
public interface GitHubService {
String ENDPOINT = "https://api.github.com";

// 獲取我的信息
@GET("/users/{user}")
Observable<UserListAdapter.GitHubUser> getUserData(@Path("user") String user);

// 獲取庫, 獲取的是數組
@GET("/users/{user}/repos")
Observable<RepoListAdapter.GitHubRepo[]> getRepoData(@Path("user") String user);
}

 

顯示用戶
顯示

詳情頁面與主頁相似, 參考代碼, 不作細說.


線程安全

Rx的好處之一就是能夠防止內存泄露, 即根據頁面生命週期, 處理異步線程的結束. 可使用RxLifecycle庫處理生命週期.

Activity類繼承RxAppCompatActivity, 替換AppCompatActivity.

啓動一個循環線程.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Rx的線程安全
* <p>
* Created by wangchenlong on 15/12/31.
*/
public class SafeActivity extends RxAppCompatActivity {
private static final String TAG = "DEBUG-WCL: " + SafeActivity.class.getSimpleName();

@Bind(R.id.simple_tv_text) TextView mTvText;

@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
ButterKnife.bind(this);

Observable.interval(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.compose(bindToLifecycle()) // 管理生命週期, 防止內存泄露
.subscribe(this::showTime);
}

private void showTime(Long time) {
mTvText.setText(String.valueOf("時間計數: " + time));
Log.d(TAG, "時間計數器: " + time);
}

@Override
protected void onPause() {
super.onPause();
Log.w(TAG, "頁面關閉!");
}
}

 

繼承RxAppCompatActivity, 添加bindToLifecycle方法管理生命週期. 當頁面onPause時, 會自動結束循環線程. 若是註釋這句代碼, 則會致使內存泄露.


RxBinding

RxBinding是Rx中處理控件異步調用的方式, 也是由Square公司開發, Jake負責編寫. 經過綁定組件, 異步獲取事件, 並進行處理. 編碼風格很是優雅.

除了RxJava, 再添加RxBinding的依賴.

1
2
3
4
// RxBinding
compile 'com.jakewharton.rxbinding:rxbinding:0.3.0'
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.3.0'
compile 'com.jakewharton.rxbinding:rxbinding-design:0.3.0'

 

Toolbar和Fab, 兩個較新的控件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".BindingActivity">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.v7.widget.Toolbar
android:id="@+id/rxbinding_t_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:popupTheme="@style/AppTheme.PopupOverlay"
tools:targetApi="21"/>

</android.support.design.widget.AppBarLayout>

<include layout="@layout/content_rxbinding"/>

<android.support.design.widget.FloatingActionButton
android:id="@+id/rxbinding_fab_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email"/>

</android.support.design.widget.CoordinatorLayout>

 

兩個EditText控件, 對比傳統方法和RxBinding方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/activity_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".BindingActivity"
tools:showIn="@layout/activity_binding">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/usual_approach"/>

<EditText
android:id="@+id/rxbinding_et_usual_approach"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="@null"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/reactive_approach"/>

<EditText
android:id="@+id/rxbinding_et_reactive_approach"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="@null"/>

<TextView
android:id="@+id/rxbinding_tv_show"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

</LinearLayout>

 

使用ButterKnife注入控件, 使用RxBinding方式綁定控件, 異步監聽事件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
* Rx綁定
* <p>
* Created by wangchenlong on 16/1/25.
*/
public class BindingActivity extends AppCompatActivity {

@Bind(R.id.rxbinding_t_toolbar) Toolbar mTToolbar;
@Bind(R.id.rxbinding_et_usual_approach) EditText mEtUsualApproach;
@Bind(R.id.rxbinding_et_reactive_approach) EditText mEtReactiveApproach;
@Bind(R.id.rxbinding_tv_show) TextView mTvShow;
@Bind(R.id.rxbinding_fab_fab) FloatingActionButton mFabFab;

@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binding);
ButterKnife.bind(this);

initToolbar(); // 初始化Toolbar
initFabButton(); // 初始化Fab按鈕
initEditText(); // 初始化編輯文本
}

// 初始化Toolbar
private void initToolbar() {
// 添加菜單按鈕
setSupportActionBar(mTToolbar);
ActionBar actionBar = getSupportActionBar();
// 添加瀏覽按鈕
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}

RxToolbar.itemClicks(mTToolbar).subscribe(this::onToolbarItemClicked);

RxToolbar.navigationClicks(mTToolbar).subscribe(this::onToolbarNavigationClicked);
}

// 點擊Toolbar的項
private void onToolbarItemClicked(MenuItem menuItem) {
String m = "點擊\"" + menuItem.getTitle() + "\"";
Toast.makeText(this, m, Toast.LENGTH_SHORT).show();
}

// 瀏覽點擊
private void onToolbarNavigationClicked(Void v) {
Toast.makeText(this, "瀏覽點擊", Toast.LENGTH_SHORT).show();
}

@Override public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_rxbinding, menu);
return super.onCreateOptionsMenu(menu);
}

// 初始化Fab按鈕
private void initFabButton() {
RxView.clicks(mFabFab).subscribe(this::onFabClicked);
}

// 點擊Fab按鈕
private void onFabClicked(Void v) {
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "點擊Snackbar", Snackbar.LENGTH_SHORT);
snackbar.show();
RxSnackbar.dismisses(snackbar).subscribe(this::onSnackbarDismissed);
}

// 銷燬Snackbar, event參考{Snackbar}
private void onSnackbarDismissed(int event) {
String text = "Snackbar消失代碼:" + event;
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}

// 初始化編輯文本
private void initEditText() {
// 正常方式
mEtUsualApproach.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) {
mTvShow.setText(s);
}

@Override public void afterTextChanged(Editable s) {

}

});

// Rx方式
RxTextView.textChanges(mEtReactiveApproach).subscribe(mTvShow::setText);
}
}

 

Toolbar使用RxToolbar監聽點擊事件; Snackbar使用RxSnackbar監聽;
EditText使用RxTextView監聽; 其他使用RxView監聽.

動畫


OK, That’s all. Enjoy it!


原始地址:
http://www.wangchenlong.org/2016/03/20/1603/207-rxjava-first/
歡迎Follow個人GitHub, 關注個人簡書微博CSDN掘金Slides.我已委託「維權騎士」爲個人文章進行維權行動. 未經受權, 禁止轉載, 受權或合做請留言.

相關文章
相關標籤/搜索