詳細(xì)解析RxAndroid的使用方式

歡迎Follow我的GitHub, 關(guān)注我的簡(jiǎn)書. 其余參考Android目錄.

Rx

本文的合集已經(jīng)編著成書,高級(jí)Android開發(fā)強(qiáng)化實(shí)戰(zhàn),歡迎各位讀友的建議和指導(dǎo)。在京東即可購(gòu)買:https://item.jd.com/12385680.html

Android

RxAndroid是RxJava的擴(kuò)展, 可以優(yōu)雅地處理異步請(qǐng)求. 以前的文章講述過一些, 這次再補(bǔ)充些內(nèi)容, 熟悉RxAndroid的使用方法.

本文源碼的GitHub下載地址

要點(diǎn)包含:
(1) 鏈?zhǔn)奖磉_(dá)式的使用方式.
(2) Lambda的應(yīng)用.
(3) Rx處理網(wǎng)絡(luò)請(qǐng)求.
(4) 線程自動(dòng)管理, 防止內(nèi)存泄露.
(5) RxBinding綁定控件的異步事件.


1. 基礎(chǔ)

當(dāng)然, 從一個(gè)嶄新的HelloWorld項(xiàng)目開始.

添加Gradle配置.

    compile 'com.jakewharton:butterknife:7.0.1'
    compile 'io.reactivex:rxandroid:1.1.0' // RxAndroid
    compile 'io.reactivex:rxjava:1.1.0' // 推薦同時(shí)加載RxJava

RxAndroid是本文的核心依賴, 同時(shí)添加RxJava. 還有ButterKnife注解庫(kù).

Lambda表達(dá)式, 是寫出優(yōu)雅代碼的關(guān)鍵, 參考.

plugins {
    id "me.tatarka.retrolambda" version "3.2.4"
}

android {
    ...

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

Gradle 2.1+以上, 配置非常簡(jiǎn)單, 添加一個(gè)plugin和一個(gè)Java1.8兼容即可.

從主MainActivity跳轉(zhuǎn)至SimpleActivity.

/**
 * 主Activity, 用于跳轉(zhuǎn)各個(gè)模塊.
 *
 * @author wangchenlong
 */
public class MainActivity extends AppCompatActivity {

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

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

    // 跳轉(zhuǎn)復(fù)雜的頁(yè)面
    public void gotoMoreModule(View view) {
        startActivity(new Intent(this, MoreActivity.class));
    }

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

    // 跳轉(zhuǎn)網(wǎng)絡(luò)的頁(yè)面
    public void gotoNetworkModule(View view) {
        startActivity(new Intent(this, NetworkActivity.class));
    }

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

SimpleActivity中, 創(chuàng)建一個(gè)觀察者, 收到字符串的返回.

    // 觀察事件發(fā)生
    Observable.OnSubscribe mObservableAction = new Observable.OnSubscribe<String>() {
        @Override public void call(Subscriber<? super String> subscriber) {
            subscriber.onNext(sayMyName()); // 發(fā)送事件
            subscriber.onCompleted(); // 完成事件
        }
    };

...

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

創(chuàng)建兩個(gè)訂閱者, 使用字符串輸出信息.

    // 訂閱者, 接收字符串, 修改控件
    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); // 設(shè)置文字
        }
    };

    // 訂閱者, 接收字符串, 提示信息
    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();
        }
    };

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

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

        // 注冊(cè)觀察活動(dòng)
        @SuppressWarnings("unchecked")
        Observable<String> observable = Observable.create(mObservableAction);

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

最基礎(chǔ)的RxAndroid使用.


基礎(chǔ)

2. 更多

我們已經(jīng)熟悉了初步的使用方式, 在接著學(xué)習(xí)一些其他方法, 如

just: 獲取輸入數(shù)據(jù), 直接分發(fā), 更加簡(jiǎn)潔, 省略其他回調(diào).
from: 獲取輸入數(shù)組, 轉(zhuǎn)變單個(gè)元素分發(fā).
map: 映射, 對(duì)輸入數(shù)據(jù)進(jìn)行轉(zhuǎn)換, 如大寫.
flatMap: 增大, 本意就是增肥, 把輸入數(shù)組映射多個(gè)值, 依次分發(fā).
reduce: 簡(jiǎn)化, 正好相反, 把多個(gè)數(shù)組的值, 組合成一個(gè)數(shù)據(jù).

來看看這個(gè)示例, 設(shè)置兩個(gè)不同類型數(shù)組, 作為輸入源, 根據(jù)不同情況分發(fā)數(shù)據(jù).

/**
 * 更多的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類似訂閱者, 設(shè)置TextView
    private Action1<String> mTextViewAction = new Action1<String>() {
        @Override public void call(String s) {
            mTvText.setText(s);
        }
    };

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

    // 設(shè)置映射函數(shù)
    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); // 映射字符串
        }
    };

    // 設(shè)置大寫字母
    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的其他方法, 只使用一個(gè)onNext.
        Observable<String> obShow = Observable.just(sayMyName());

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

        // 單獨(dú)顯示數(shù)組中的每個(gè)元素
        Observable<String> obMap = Observable.from(mManyWords);

        // 映射之后分發(fā)
        obMap.observeOn(AndroidSchedulers.mainThread())
                .map(mUpperLetterFunc).subscribe(mToastAction);

        // 優(yōu)化過的代碼, 直接獲取數(shù)組, 再分發(fā), 再合并, 再顯示toast, Toast順次執(zhí)行.
        Observable.just(mManyWordList)
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(mOneLetterFunc)
                .reduce(mMergeStringFunc)
                .subscribe(mToastAction);
    }

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

這次簡(jiǎn)化調(diào)用代碼, 因?yàn)橛袝r(shí)候我們對(duì)異常并不是很關(guān)心,
只要能catch異常即可, 因此流僅僅關(guān)注真正需要的部分.

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

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

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

just可以非常簡(jiǎn)單的獲取任何數(shù)據(jù), 分發(fā)時(shí), 選擇使用的線程.
map是對(duì)輸入數(shù)據(jù)加工, 轉(zhuǎn)換類型, 輸入Func1, 準(zhǔn)換大寫字母.
Func1代表使用一個(gè)參數(shù)的函數(shù), 前面是參數(shù), 后面是返回值.
Action1代表最終動(dòng)作, 因而不需要返回值, 并且一個(gè)參數(shù).

輸入數(shù)組, 單獨(dú)分發(fā)數(shù)組中每一個(gè)元素, 轉(zhuǎn)換大寫, 輸入Toast連續(xù)顯示.

        // 單獨(dú)顯示數(shù)組中的每個(gè)元素
        Observable<String> obMap = Observable.from(mManyWords);

        // 映射之后分發(fā)
        obMap.observeOn(AndroidSchedulers.mainThread())
                .map(mUpperLetterFunc).subscribe(mToastAction);

from是讀取數(shù)組中的值, 每次單獨(dú)分發(fā), 并分發(fā)多次, 其余類似.

輸入數(shù)組, 映射為單獨(dú)分發(fā), 并組合到一起, 集中顯示.

        // 優(yōu)化過的代碼, 直接獲取數(shù)組, 再分發(fā), 再合并, 再顯示toast, Toast順次執(zhí)行.
        Observable.just(mManyWordList)
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(mOneLetterFunc)
                .reduce(mMergeStringFunc)
                .subscribe(mToastAction);

這次是使用just分發(fā)數(shù)組, 則分發(fā)數(shù)據(jù)就是數(shù)組, 并不是數(shù)組中的元素.
flatMap把數(shù)組轉(zhuǎn)換為單獨(dú)分發(fā), Func1內(nèi)部使用from拆分?jǐn)?shù)組.
reduce把單獨(dú)分發(fā)數(shù)據(jù)集中到一起, 再統(tǒng)一分發(fā), 使用Func2.
最終使用Action1顯示獲得數(shù)據(jù). 本次代碼也更加簡(jiǎn)潔.

由此我們可以觀察到, Rx的寫法可以是多種多樣, 合理的寫法會(huì)更加優(yōu)雅.

效果


效果

3. Lambda

Lambda表達(dá)式和Rx非常契合, 可以省略大量的內(nèi)部類, 如Func和Action.
我們把上個(gè)示例, 用Lambda再寫一次, 功能相同.

/**
 * Lambda表達(dá)式寫法
 * <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的其他方法, 只使用一個(gè)onNext.
        Observable<String> obShow = Observable.just(sayMyName());

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

        // 單獨(dú)顯示數(shù)組中的每個(gè)元素
        Observable<String> obMap = Observable.from(mManyWords);

        // 映射之后分發(fā)
        obMap.observeOn(AndroidSchedulers.mainThread())
                .map(String::toUpperCase)
                .subscribe(this::showToast);

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

    // 創(chuàng)建字符串
    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);
    }
}

這次沒有使用常規(guī)的Lambda表達(dá)式, 而是更簡(jiǎn)單的方法引用(Method References).
方法引用: 方法參數(shù)和返回值與Lambda表達(dá)式相同時(shí), 使用方法名代替.


4. 網(wǎng)絡(luò)請(qǐng)求

Retrofit是網(wǎng)絡(luò)請(qǐng)求庫(kù), 剛推出2.0版本. Rx的一個(gè)核心應(yīng)用就是處理異步網(wǎng)絡(luò)請(qǐng)求, 結(jié)合Retrofit, 會(huì)更加方便和簡(jiǎn)潔. 參考.

引入庫(kù)

    compile 'com.android.support:recyclerview-v7:23.1.1' // RecyclerView
    
    compile 'com.squareup.retrofit:retrofit:2.0.0-beta2' // Retrofit網(wǎng)絡(luò)處理
    compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2' // Retrofit的rx解析庫(kù)
    compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2' // Retrofit的gson庫(kù)

    compile 'com.squareup.picasso:picasso:2.5.2' // Picasso網(wǎng)絡(luò)圖片加載

recyclerviewpicasso為了顯示. retrofit系列是網(wǎng)絡(luò)請(qǐng)求.

主頁(yè)使用一個(gè)簡(jiǎn)單的列表視圖, 展示Github的用戶信息.

/**
 * Rx的網(wǎng)絡(luò)請(qǐng)求方式
 * <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);

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

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

    // 點(diǎn)擊的回調(diào)
    public interface UserClickCallback {
        void onItemClicked(String name);
    }

    // 跳轉(zhuǎn)到庫(kù)詳情頁(yè)面
    private void gotoDetailPage(String name) {
        startActivity(NetworkDetailActivity.from(NetworkActivity.this, name));
    }
}

在列表中提供點(diǎn)擊用戶信息跳轉(zhuǎn)至用戶詳情.
NetworkWrapper.getUsersInto(adapter) 請(qǐng)求網(wǎng)絡(luò), 設(shè)置適配器信息.

關(guān)鍵部分, 適配器, 其中包含ViewHolder類和數(shù)據(jù)類.

/**
 * 顯示列表
 * <p>
 * Created by wangchenlong on 15/12/31.
 */
public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.UserViewHolder> {

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

    private NetworkActivity.UserClickCallback mCallback; // 用戶點(diǎn)擊項(xiàng)的回調(diào)

    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);
            // 綁定點(diǎn)擊事件
            itemView.setOnClickListener(v ->
                    callback.onItemClicked(mTvUserLogin.getText().toString()));
        }

        // 綁定數(shù)據(jù)
        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;
    }
}

添加數(shù)據(jù)addUser, 其中notifyItemInserted通知更新.
可以自動(dòng)生成Json解析類的網(wǎng)站.

首先創(chuàng)建`Retrofit``服務(wù), 通過服務(wù)獲取數(shù)據(jù), 再依次分發(fā)給適配器.

/**
 * 用戶獲取類
 * <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);
    }

    // 獲取庫(kù)信息
    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);
    }
}

網(wǎng)絡(luò)請(qǐng)求無法在主線程上執(zhí)行, 需要啟動(dòng)異步線程, 如Schedulers.newThread().
使用工廠模式ServiceFactory創(chuàng)建服務(wù), 也可以單獨(dú)創(chuàng)建服務(wù).

創(chuàng)建Retrofit服務(wù)的工廠類.

/**
 * 工廠模式
 * <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轉(zhuǎn)換器
                .build();
        return adapter.create(serviceClass);
    }
}

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

設(shè)置網(wǎng)絡(luò)請(qǐng)求的Url.

/**
 * GitHub的服務(wù)
 * <p>
 * Created by wangchenlong on 15/12/31.
 */
public interface GitHubService {
    String ENDPOINT = "https://api.github.com";

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

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

顯示用戶


顯示

詳情頁(yè)面與主頁(yè)類似, 參考代碼, 不做細(xì)說.


5. 線程安全

Rx的好處之一就是可以防止內(nèi)存泄露, 即根據(jù)頁(yè)面生命周期, 處理異步線程的結(jié)束. 可以使用RxLifecycle庫(kù)處理生命周期.

Activity類繼承RxAppCompatActivity, 替換AppCompatActivity.

啟動(dòng)一個(gè)循環(huán)線程.

/**
 * 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()) // 管理生命周期, 防止內(nèi)存泄露
                .subscribe(this::showTime);
    }

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

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

繼承RxAppCompatActivity, 添加bindToLifecycle方法管理生命周期. 當(dāng)頁(yè)面onPause時(shí), 會(huì)自動(dòng)結(jié)束循環(huán)線程. 如果注釋這句代碼, 則會(huì)導(dǎo)致內(nèi)存泄露.


6. RxBinding

RxBinding是Rx中處理控件異步調(diào)用的方式, 也是由Square公司開發(fā), Jake負(fù)責(zé)編寫. 通過綁定組件, 異步獲取事件, 并進(jìn)行處理. 編碼風(fēng)格非常優(yōu)雅.

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

    // 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, 兩個(gè)較新的控件.

<?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>

兩個(gè)EditText控件, 對(duì)比傳統(tǒng)方法和RxBinding方法.

<?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方式綁定控件, 異步監(jiān)聽事件.

/**
 * 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);
    }

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

    // 瀏覽點(diǎn)擊
    private void onToolbarNavigationClicked(Void v) {
        Toast.makeText(this, "瀏覽點(diǎn)擊", 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);
    }

    // 點(diǎn)擊Fab按鈕
    private void onFabClicked(Void v) {
        Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "點(diǎn)擊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監(jiān)聽點(diǎn)擊事件; Snackbar使用RxSnackbar監(jiān)聽;
EditText使用RxTextView監(jiān)聽; 其余使用RxView監(jiān)聽.

動(dòng)畫

OK, That's all! Enjoy It!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容