RxAndroid是RxJava的扩展, 优雅地处理异步请求. RxAndroid配合Lambda表达式, 精简处理回调, 使程序更具有可读性. Rx作为Android最优秀的开源库之一, 极大地提高生产力, 我们需要掌握. 本文由浅入深, 介绍一些常见的使用方法, 并附有源码.

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

本文代码的GitHub下载地址.

要点包含:
(1) 链式表达式的使用方式.
(2) Lambda的应用.
(3) Rx处理网络请求.
(4) 线程自动管理, 防止内存泄露.
(5) RxBinding绑定控件的异步事件.


基础

当然, 从一个崭新的HelloWorld项目开始.

添加Gradle配置.

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注解库.

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.
我已委托“维权骑士”为我的文章进行维权行动. 未经授权, 禁止转载, 授权或合作请留言.

详细解析 RxAndroid 的使用方式的更多相关文章

  1. 【详细解析】MySQL索引详解( 索引概念、6大索引类型、key 和 index 的区别、其他索引方式)

    [详细解析]MySQL索引详解( 索引概念.6大索引类型.key 和 index 的区别.其他索引方式) MySQL索引的概念: 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分 ...

  2. java类生命周期详细解析

    (一)详解java类的生命周期 引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前 ...

  3. springmvc 项目完整示例06 日志–log4j 参数详细解析 log4j如何配置

    Log4j由三个重要的组件构成: 日志信息的优先级 日志信息的输出目的地 日志信息的输出格式 日志信息的优先级从高到低有ERROR.WARN. INFO.DEBUG,分别用来指定这条日志信息的重要程度 ...

  4. 对MySQL DELETE语法的详细解析

    以下的文章主要描述的是MySQL DELETE语法的详细解析,首先我们是从单表语法与多表语法的示例开始的,假如你对MySQL DELETE语法的相关内容十分感兴趣的话,你就可以浏览以下的文章对其有个更 ...

  5. 转:二十一、详细解析Java中抽象类和接口的区别

    转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...

  6. 单表扫描,MySQL索引选择不正确 并 详细解析OPTIMIZER_TRACE格式

    单表扫描,MySQL索引选择不正确 并 详细解析OPTIMIZER_TRACE格式     一 表结构如下:  万行 CREATE TABLE t_audit_operate_log (  Fid b ...

  7. 在PHP中使用CURL,“撩”服务器只需几行——php curl详细解析和常见大坑

    在PHP中使用CURL,"撩"服务器只需几行--php curl详细解析和常见大坑 七夕啦,作为开发,妹子没得撩就"撩"下服务器吧,妹子有得撩的同学那就左拥妹子 ...

  8. 微信消息体签名及加解密功能详细解析以及.net实现

    原文:微信消息体签名及加解密功能详细解析以及.net实现 前言 微信消息体签名及加密功能已上线,明文传输确实存在安全风险,鉴于微信的用户范围使用之广泛,必定会成为众矢之的.所以大家还是尽快接入安全模式 ...

  9. 详细解析Linux scp命令的应用

    详细解析Linux scp命令的应用 Linux命令有人统计说是有4000多个,Linux scp命令是用于Linux之间复制文件和目录,这里详细介绍scp命令使用和参数. AD: Linux scp ...

随机推荐

  1. NGINX结合SHELL统计用户的UV及IP汇总

    公司新的需求,怀疑PV和IP的比例不对,看是不是有恶意的东东: NGINX配置: log_format main '$remote_addr - $remote_user [$time_local] ...

  2. 【转】中断处理函数中不用disable_irq而用disable_irq_nosync原因

    原文网址:http://blog.csdn.net/skyflying2012/article/details/8265869 今天在写触摸屏驱动时在中断处理函数中使用disable_irq关中断发现 ...

  3. 《Node.js开发指南》知识整理

    Node.js简介 Node是一个可以让JavaScript运行在服务器端的平台,抛弃了传统平台依靠多线程来实现高并发的设计思路,而采用单线程.异步式I/O.事件驱动式的程序设计模型. 安装和配置No ...

  4. 程序设计实习MOOC / 继承和派生——编程作业 第五周程序填空题1

    描述 写一个MyString 类,使得下面程序的输出结果是: 1. abcd-efgh-abcd- 2. abcd- 3. 4. abcd-efgh- 5. efgh- 6. c 7. abcd- 8 ...

  5. openstack 手动 部署安装调试

    Virtual Interface creation failed

  6. java类的加载顺序

    related URL: http://www.cnblogs.com/guoyuqiangf8/archive/2012/10/31/2748909.html Parent Class: packa ...

  7. git与svn对比

    git 与 svn 对比 git的使用不需要联机 SVN集中式版本控制:每个人的版本都是提交到服务器,服务器坏了就雪崩.git分布式版本控制:  安全,每人本地有个版本库,每个人都可以充当‘服务器 它 ...

  8. React Native 初识

    Facebook 在 React.js Conf 2015 大会上推出了基于 JavaScript 的开源框架 React Native,本中文教程翻译自 React Native 官方文档. Rea ...

  9. JavaScript 定义类

    ES6以前: function Point(x, y) { this.x = x; this.y = y; } Point.prototype.hello= function () { return ...

  10. Android adb

    查看原文:http://blog.csdn.net/u010818425/article/details/52266593 (一)基础操作 安装app adb install -r xxx.apk / ...