iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 15
0
自我挑戰組

Android初學筆記系列 第 15

Day 15 - EventBus

  • 分享至 

  • xImage
  •  

今天要介紹一個非常簡單好用的第三方套件EventBus,從有參加鐵人賽的念頭時就想說一定要寫它,恰好最近改版到3.0,現在寫可以跟前輩們的2.X介紹文有點區隔,完美。

今天的練習會跟昨天的RecyclerView結合,當我們點選時把項目名稱交給Activity更新主畫面。

簡介

EventBus是一個觀察者模式的套件,常見的用途是「跨程式溝通」
http://ithelp.ithome.com.tw/upload/images/20161231/20103849PsaYZhUPOe.png
(來自官方Github)

例如在Activity註冊一個觀察者(Subscriber),之後在其他程式用發佈者(Publisher)發出訊息,Activity就會收到訊息,我們再依訊息做相應動作。

看完以上還是不懂沒關係,我當初看也沒有懂,待會看程式碼會更了解運作方式,並發現它真的很簡單!


加入dependencies

在gradle加入:

compile 'org.greenrobot:eventbus:3.0.0'

忘記怎麼加入的可以參考Day13有圖解


建立Event

Event可以是任意class,我們建個簡單的MessageEvent,裡面帶有一個字串

public class MessageEvent {

    private String Message;

    public MessageEvent(String message) {
        this.Message = message;
    }

    public String getMessage() {
        return Message;
    }
}

註冊Subscribe

一般在Activity中註冊會像這樣

public class MainActivity extends AppCompatActivity {

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

    @Override
    public void onStart() {
        super.onStart();
        // 在此Activity啟用EventBus
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        // 在Activity停用EventBus,讓Subscribe停止接收
        EventBus.getDefault().unregister(this);
    }

    // 註冊Subscribe,觀察目標為MessageEvent
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        // 收到MessageEvent時要做的事寫在這裡
    };
}

在onStart開始觀察,並在onStop停止觀察,因onStop時Activity已經不在畫面上了,停止觀察可以避免多餘的運作,可以參考Day11 - Activity生命週期


Publisher發送事件

只要一行程式,不需要任何準備動作

EventBus.getDefault().post(new MessageEvent("要發送的訊息"));

當這行程式執行時,所有觀察目標為MessageEvent的Subscribe就會收到訊息了,接著就把EventBus加入昨天的專案中


與RecyclerView結合

延續昨天的專案,若我們點擊列表時想在主畫面用TextView顯示出項目名稱,此時會發現點擊事件寫在MyAdapter裡,而要更新的TextView卻在MainActivity,要怎麼讓這兩個程式溝通就可以靠EventBus來實現

將publisher放到ViewHolder點擊事件裡

class ViewHolder extends RecyclerView.ViewHolder{

        ViewHolder(View itemView) {
            super(itemView);
            
            // 略..

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // 取得項目名稱
                    String msg = mData.get(getAdapterPosition());
                    // 發出MessageEvent
                    EventBus.getDefault().post(new MessageEvent(msg));
                }
            });
        }
    }

activity_main新增一個TextView用來顯示收到的訊息

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <TextView
        android:id="@+id/txtTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="22sp"/>

    <Button
        android:id="@+id/btnAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/txtTitle"
        android:text="新增"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnAdd"/>

</RelativeLayout>

MainActivity的Subscribe中更新TextView

public class MainActivity extends AppCompatActivity {

    private TextView txtTitle;
    // 略..

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

        txtTitle = (TextView) findViewById(R.id.txtTitle);
        
        //略..
    }

    @Override
    public void onStart() {
        super.onStart();
        // 在此Activity啟用EventBus
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        // 在Activity停用EventBus,讓Subscribe停止接收
        EventBus.getDefault().unregister(this);
    }

    // 註冊Subscribe,觀察目標為MessageEvent
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        // 收到MessageEvent時將內容顯示出來
        txtTitle.setText("選擇的是:" + event.getMessage());
    };

完成,點選項目時ViewHolder會發出MessageEvent,Activity收到並將內容顯示出來,就完成跨程式的溝通囉
http://ithelp.ithome.com.tw/upload/images/20170101/20103849hJqePoqXWJ.png


EventBus使用上非常簡潔,除了Reddit討論串建議如本篇的方式和RecyclerView搭配使用之外,將來若有開發到Fragment或Service要與Activity互相溝通時更是出奇的好用,建議大家都可以試用看看哦。

2018 Update:

使用EventBus來做RecyclerView點擊事件是不好的方式,一對一的程式互動應盡量使用interface等方式來處理,需要一對多時才考慮EventBus,因為EventBus散落在專案各處將使專案擴大後難以維護。interface的建立方式請參考昨天文章的結尾處。


上一篇
Day 14 - 使用RecyclerView(2)
下一篇
Day 16 - Design Support Library
系列文
Android初學筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言