Android 基于MediatorLiveData實現(xiàn)紅點的統(tǒng)一管理
背景
小紅點在各個App內(nèi)隨處可見,并且隨著需求的不斷迭代,需要展示小紅點的需求越來越多。
- 不同需求之間,紅點顯示可能有沖突。
- 不同頁面之間,紅點顯示會有關(guān)聯(lián)。
- 同一個紅點,可能顯示成數(shù)字樣式,紅點樣式,文案樣式。
這個時候,如果沒有對紅點的展示邏輯做一個統(tǒng)一的抽象和管理的話,就會感覺很復雜,后續(xù)也不太好維護。
本文會基于MediatorLiveData,實現(xiàn)對紅點的統(tǒng)一管理。
需求分析
這里舉個例子,常見的紅點場景,類似qq首頁左上角紅點的顯示。
- 4個頁面,由首頁進入到隱私保護指引頁面,都有相應的紅點View顯示,去引導用戶進入到最里面的"隱私保護指引"頁面。
- 當用戶點擊紅點,進入到"隱私保護指引"頁面后,隱私保護指引對應的紅點就會消失,同時會觸發(fā)上級頁面的紅點刷新。


思路分析
樹形模型
一個App的頁面本身就是分級的,對于頁面的訪問路徑本質(zhì)上就是個樹型結(jié)構(gòu)。 整體的實現(xiàn)思路是用樹形模型去管理不同頁面的紅點。
- 每個小紅點就是一個樹的節(jié)點,父節(jié)點的小紅點是否顯示,取決于它的子節(jié)點的并集結(jié)果。
- 同一個頁面中的不同紅點。在樹中是同一個層級,屬于兄弟關(guān)系,互相獨立。
- 子節(jié)點的狀態(tài)變化,會遞歸地去觸發(fā)父節(jié)點的狀態(tài)變化。

具體代碼實現(xiàn)
那對應的代碼應該如何實現(xiàn)呢,難道真的要手動自己實現(xiàn)一棵樹?也不是不行,就是感覺有點小麻煩的樣子。 下面進入正題。
MediatorLiveData
官方提供了MediatorLiveData。
- 通過addSource方法,可以監(jiān)聽另一個LiveData的數(shù)據(jù)變化
- 本身就是一個LiveData,可以被其他Observer觀察
這兩個特點,剛好滿足我們的需求實現(xiàn)。比如MediatorLiveData A觀察 MediatorLiveData B,MediatorLiveData B觀察 MediatorLiveData C 和MediatorLiveData D。并且被觀察的LiveData發(fā)生變化,作為觀察的LiveData都能收到通知。
通過管理多個LiveData之間的關(guān)系,這樣就可以間接實現(xiàn)了一棵樹的模型。

public class MediatorLiveData<T> extends MutableLiveData<T> {
private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
//使用Source包一下
Source<S> e = new Source<>(source, onChanged);
Source<?> existing = mSources.putIfAbsent(source, e);
if (hasActiveObservers()) {
e.plug();
}
}
private static class Source<V> implements Observer<V> {
final LiveData<V> mLiveData;
final Observer<? super V> mObserver;
int mVersion = START_VERSION;
Source(LiveData<V> liveData, final Observer<? super V> observer) {
mLiveData = liveData;
mObserver = observer;
}
void plug() {
mLiveData.observeForever(this);
}
void unplug() {
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
mObserver.onChanged(v);
}
}
}
}
RedPointManager
- 這里的實現(xiàn),封裝成一個單例RedPointManager,暴露相應的紅點數(shù)據(jù)給外部。
- LiveData數(shù)據(jù)驅(qū)動:RedPointManager內(nèi)包含了多個LiveData,不同頁面的紅點View可以通過觀察對應的LiveData,來驅(qū)動自身的視圖變化。
- 父節(jié)點使用MediatorLiveData,觀察相應的子節(jié)點LiveData。 葉子節(jié)點定義成普通的LiveData就行了,因為不需要觀察其他對象了。

/**
* 基于MediatorLiveData,實現(xiàn)樹形紅點管理
*/
class RedPointManager : IRedPointManager {
companion object {
val TAG = "RedPointManager"
@JvmStatic
val instance: IRedPointManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
RedPointManager()
}
}
override val liveDataA = MediatorLiveData<Boolean>()
override val liveDataB1 = MediatorLiveData<Boolean>()
override val liveDataB2 = MutableLiveData<Boolean>()
override val liveDataC1 = MutableLiveData<Boolean>()
override val liveDataC2 = MutableLiveData<Boolean>()
init {
Log.d(TAG, "RedPointManager init")
/**
* 構(gòu)建樹型關(guān)系。按路徑層級,進行觀察。一般外部只需要改動最低層的紅點對應的LiveData,頂部的LiveData就會自動改變
*/
liveDataA.addSource(liveDataB1, Observer {
liveDataA.postValue(liveDataB1.isTrue() || liveDataB2.isTrue())
})
liveDataA.addSource(liveDataB2, Observer {
liveDataA.postValue(liveDataB1.isTrue() || liveDataB2.isTrue())
})
liveDataB1.addSource(liveDataC1, Observer {
liveDataB1.postValue(liveDataC1.isTrue() || liveDataC2.isTrue())
})
liveDataB1.addSource(liveDataC2, Observer {
liveDataB1.postValue(liveDataC1.isTrue() || liveDataC2.isTrue())
})
}
override fun testChangeDataC1(show: Boolean) {
liveDataC1.postValue(show)
Log.d(TAG, "testChangeDataC1: $show")
}
}
/**
* 定義接口
* 提供只讀的屬性,提供修改liveData的方法
*/
interface IRedPointManager {
val liveDataA: LiveData<Boolean>
val liveDataB1: LiveData<Boolean>
val liveDataB2: LiveData<Boolean>
val liveDataC1: LiveData<Boolean>
val liveDataC2: LiveData<Boolean>
fun testChangeDataC1(show: Boolean)
}
驗證刷新邏輯
一般情況下,只需要改動葉子節(jié)點的紅點對應的LiveData,父節(jié)點的LiveData就會自動改變。 基于上述代碼,調(diào)用testChangeDataC1方法后,監(jiān)聽LiveData并輸出日志。
private fun testRedPointManager() {
RedPointManager.instance.liveDataA.observe(this, Observer {
Log.d(TAG, "liveDataA: $it")
})
RedPointManager.instance.liveDataB1.observe(this, Observer {
Log.d(TAG, "liveDataB1: $it")
})
RedPointManager.instance.liveDataB2.observe(this, Observer {
Log.d(TAG, "liveDataB2: $it")
})
RedPointManager.instance.liveDataC1.observe(this, Observer {
Log.d(TAG, "liveDataC1: $it")
})
RedPointManager.instance.liveDataC2.observe(this, Observer {
Log.d(TAG, "liveDataC2: $it")
})
RedPointManager.instance.testChangeDataC1(true)
}
//從輸出日志可以發(fā)現(xiàn),底層的liveDataC1發(fā)生改變,觸發(fā)頂層的liveDataB1發(fā)生改變。liveDataB1的變化,也觸發(fā)了liveDataA發(fā)生改變。
RedPointManager init
testChangeDataC1: true
liveDataC1: true
lveDataB1: true
liveDataA: true
總結(jié)
到這里就結(jié)束了,App端內(nèi)實現(xiàn)紅點的統(tǒng)一管理,如果有類似的場景,可以用這種思路去實現(xiàn)。 上面的例子比較簡單,更復雜的場景,應該也是可以基于上面的方案進行改造一下的。
以上就是Android 基于MediatorLiveData實現(xiàn)紅點的統(tǒng)一管理的詳細內(nèi)容,更多關(guān)于MediatorLiveData實現(xiàn)紅點的統(tǒng)一管理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實現(xiàn)五子棋游戲(局域網(wǎng)版)
這篇文章主要為大家詳細介紹了Android實現(xiàn)局域網(wǎng)版的五子棋游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05
Android編程使用ListView實現(xiàn)數(shù)據(jù)列表顯示的方法
這篇文章主要介紹了Android編程使用ListView實現(xiàn)數(shù)據(jù)列表顯示的方法,實例分析了Android中ListView控件的使用技巧,需要的朋友可以參考下2016-01-01
Android動畫之3D翻轉(zhuǎn)效果實現(xiàn)函數(shù)分析
Android中的翻轉(zhuǎn)動畫效果的實現(xiàn),Android中并沒有提供直接做3D翻轉(zhuǎn)的動畫,所以關(guān)于3D翻轉(zhuǎn)的動畫效果需要我們自己實現(xiàn),那么我們首先來分析一下Animation 和 Transformation,感興趣的朋友可以了解下啊2013-01-01
Android Parcleable接口的調(diào)用源碼層分析
這篇文章主要給大家介紹了關(guān)于利用Kotlin如何實現(xiàn)Android開發(fā)中的Parcelable的相關(guān)資料,并且給大家介紹了關(guān)于Android Parcleable源碼層問題,需要的朋友可以參考下2022-12-12
sqlite查詢結(jié)果在listview中展示的實現(xiàn)
下面小編就為大家?guī)硪黄猻qlite查詢結(jié)果在listview中展示的實現(xiàn)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04
新版Android studio導入微信支付和支付寶官方Demo問題解決大全
這篇文章主要為大家詳細介紹了新版Android studio導入微信支付和支付寶官方Demo問題的解決大全,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-07-07

