第二種做法,在做之前要先了解一下
叫做觀察者模式
當多個 Class 都需要接收同一種資料的變化時,就適合使用 Observer Pattern
---參考自Design Pattern
這個練習的專案就像上面這句話
各顯示的fragment就是觀察者,同樣需要接收來自MainActivity查詢的資料變化以更新畫面
MainActivity(被觀察者,目標)提供訂閱,當有資料更新時發出通知
fragment(觀察者)可訂閱或退訂,來決定要不要接收通知
在改寫天氣的專案前,先試著做個觀察者模式的app練習理解看看
假設上方中間的爲(被觀察者),下方左右二個爲(觀察者)
當中間欄位輸入資料,按下更新
下方觀察者若有訂閱的話,也會跟著更新
常見的觀察者模式有包含觀察者與被觀察者的介面
建立一個包含3種方法的interface
分別是
interface IPublisher {
fun getTitleChanged(title: String)
fun add(subscriber: IObserver)
fun remove(subscriber: IObserver)
}
建立一個可以收到通知的interface
interface IObserver {
fun update(targetTitle: String)
}
建立被觀察者的class並實作interface IPublisher
class TargetObject : IPublisher {
private val subScribers = mutableListOf<IObserver>()
override fun getTitleChanged(title: String) {
subScribers.forEach { it.update(title) }
}
override fun add(subscriber: IObserver) {
subScribers.add(subscriber)
}
override fun remove(subscriber: IObserver) {
subScribers.remove(subscriber)
}
}
原本我建立了觀察者的class並實作interface IObserver
class Observer : IObserver{
var observerTitle = "observer"
override fun update(targetTitle: String) {
observerTitle =targetTitle
}
再到MainActivity實例化,並且每次target更新時,將實例化的屬性再給textview
val observerOne = Observer()
...
btn_update.setOnClickListener {
target.getTitleChanged(ed_target.text.toString())
tv_observer1.text = observerOne.observerTitle
}
請教導師後,得到建議的更好做法,以這個練習來說
可使用物件表示式,以匿名物件的方式直接實作interface的update
private val observerOne = object : IObserver {
override fun update(targetTitle: String) {
tv_observer1.text = targetTitle
}
}
這樣不需要特別建立一個觀察者的class
並且在目標更新的時候,資料傳入觀察者的update方法就可以直接給textview顯示了
var checkScriberOne = false
btn_scriber1.setOnClickListener {
when (checkScriberOne) {
false -> {
target.add(observerOne)
btn_scriber1.text = "已訂閱"
checkScriberOne = true
btn_scriber1.setTextColor(Color.RED)
}
true -> {
target.remove(observerOne)
btn_scriber1.text = "未訂閱"
checkScriberOne = false
btn_scriber1.setTextColor(Color.BLACK)
}
}
}
執行的結果像是這樣