前言
小紅點(diǎn)提示定硝,可以說是現(xiàn)在app都會有的一個功能,比如微信的消息界面毫目,微博的我的界面。一般來說诲侮,一個小紅點(diǎn)不會單獨(dú)存在镀虐,可能會和其他紅點(diǎn)之間有所關(guān)聯(lián),構(gòu)成紅點(diǎn)系統(tǒng)沟绪。
紅點(diǎn)系統(tǒng)可以看做是樹形結(jié)構(gòu)刮便,紅點(diǎn)系統(tǒng)設(shè)計(jì)遵循以下原則:
- 子節(jié)點(diǎn)上的紅點(diǎn)變化,需要通知其父節(jié)點(diǎn)更新
- 父節(jié)點(diǎn)上紅點(diǎn)數(shù)量绽慈,是其所有子節(jié)點(diǎn)紅點(diǎn)數(shù)量之和
- 父節(jié)點(diǎn)上紅點(diǎn)清除恨旱,要將其所有子節(jié)點(diǎn)紅點(diǎn)清除(數(shù)量置為0)
之前我們小紅點(diǎn)是這么處理的
當(dāng)收到紅點(diǎn)消息時,通過EventBus方式通知對應(yīng)的紅點(diǎn)view更新坝疼,同時需要發(fā)送event通知其父節(jié)點(diǎn)搜贤,父節(jié)點(diǎn)收到通知需要計(jì)算它所有子節(jié)點(diǎn)紅點(diǎn)數(shù)量之和,然后再更新自身钝凶,如果該父節(jié)點(diǎn)還有父節(jié)點(diǎn)仪芒,需要再發(fā)送一個event......
可以看出,這是一個非常糟糕的設(shè)計(jì)耕陷,可以說毫無設(shè)計(jì)掂名。這在紅點(diǎn)數(shù)量少的時候,感覺不到問題哟沫,但是隨著版本不斷迭代饺蔑,需要的紅點(diǎn)越來越多,就會發(fā)現(xiàn)它是多么的糟糕嗜诀。代碼中存在大量的event來處理紅點(diǎn)猾警,收到一個紅點(diǎn)消息,可能要發(fā)好幾個event裹虫,代碼邏輯混亂肿嘲,增加刪除紅點(diǎn)復(fù)雜,如果不熟悉業(yè)務(wù)邏輯筑公,很容易漏掉相關(guān)代碼雳窟。
后來我是這么設(shè)計(jì)的
基于觀察者模式,設(shè)計(jì)下面兩個類
1.紅點(diǎn)處理類
- 維護(hù)自身的key,父節(jié)點(diǎn)key封救,子節(jié)點(diǎn)key(如果有的話)
- 處理tcp推過來的消息拇涤,通知觀察者更新視圖,并通知父節(jié)點(diǎn)更新
- 清除紅點(diǎn)消息誉结,并通知父節(jié)點(diǎn)更新鹅士,同時清除子節(jié)點(diǎn)紅點(diǎn)
2.紅點(diǎn)中心類
- 注冊紅點(diǎn)處理類
- 注冊觀察者(向紅點(diǎn)處理類注冊觀察者)
- 接收tcp發(fā)送的紅點(diǎn)信息,然后分發(fā)給對應(yīng)紅點(diǎn)處理類
- 接收清除紅點(diǎn)信息惩坑,然后分發(fā)給對應(yīng)的紅點(diǎn)處理類
這樣設(shè)計(jì)的好處是掉盅,低耦合,代碼邏輯清晰以舒,增加刪除紅點(diǎn)方便趾痘,符合數(shù)據(jù)驅(qū)動設(shè)計(jì)思想,每次有紅點(diǎn)消息變化時蔓钟,只要更新對應(yīng)的紅點(diǎn)數(shù)據(jù)就可以了永票。
LiveData
后來接觸了LiveData,覺得代碼可以更簡單一點(diǎn)滥沫,LiveData本身就是觀察者模式侣集,還有生命周期處理等其他好處,用它來處理紅點(diǎn)應(yīng)該會很合適兰绣。每個紅點(diǎn)對應(yīng)一個LiveData世分,如果該紅點(diǎn)是一個父節(jié)點(diǎn),則使用MediatorLiveData
官網(wǎng)上是這么描述的
MediatorLiveData
是LiveData
的子類缀辩,允許您合并多個 LiveData 源罚攀。只要任何原始的 LiveData 源對象發(fā)生更改,就會觸發(fā)MediatorLiveData
對象的觀察者雌澄。
這樣我們可以寫一個全局的ViewModel斋泄,里面存放這些紅點(diǎn)對應(yīng)的LiveData,紅點(diǎn)view所在的界面镐牺,observer這些LiveData炫掐,當(dāng)收到紅點(diǎn)消息時,直接LiveData.setValue就行睬涧,這樣代碼邏輯上會很簡單募胃。
寫了一個小demo
ViewModel
class MainViewModel : ViewModel() {
val systemNum = MutableLiveData<Int>(0)
val friendNum = MutableLiveData<Int>(0)
val otherNum = MutableLiveData<Int>(0)
val totalNum = MediatorLiveData<Int>()
init {
totalNum.addSource(systemNum){
totalNum.value = systemNum.value!! + friendNum.value!! + otherNum.value!!
}
totalNum.addSource(friendNum){
totalNum.value = systemNum.value!! + friendNum.value!! + otherNum.value!!
}
totalNum.addSource(otherNum){
totalNum.value = systemNum.value!! + friendNum.value!! + otherNum.value!!
}
}
fun clear(){
systemNum.value = 0
friendNum.value = 0
otherNum.value = 0
}
}
Fragment
class MainFragment : Fragment() {
companion object {
fun newInstance() = MainFragment()
}
private lateinit var viewModel: MainViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.main_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
viewModel.systemNum.observe(viewLifecycleOwner){
view.findViewById<TextView>(R.id.tv_message_system).text = it.toString()
}
viewModel.friendNum.observe(viewLifecycleOwner){
view.findViewById<TextView>(R.id.tv_message_friend).text = it.toString()
}
viewModel.otherNum.observe(viewLifecycleOwner){
view.findViewById<TextView>(R.id.tv_message_other).text = it.toString()
}
viewModel.totalNum.observe(viewLifecycleOwner){
view.findViewById<TextView>(R.id.tv_message_all).text = it.toString()
}
view.findViewById<Button>(R.id.bt_add_system).setOnClickListener {
//系統(tǒng)消息加
viewModel.systemNum.value = viewModel.systemNum.value!!+1
}
view.findViewById<Button>(R.id.bt_reduce_system).setOnClickListener {
//系統(tǒng)消息減
if (viewModel.systemNum.value!! > 0){
viewModel.systemNum.value = viewModel.systemNum.value!!-1
}
}
view.findViewById<Button>(R.id.bt_add_friend).setOnClickListener {
//好友消息加
viewModel.friendNum.value = viewModel.friendNum.value!!+1
}
view.findViewById<Button>(R.id.bt_reduce_friend).setOnClickListener {
//好友消息減
if (viewModel.friendNum.value!! > 0){
viewModel.friendNum.value = viewModel.friendNum.value!!-1
}
}
view.findViewById<Button>(R.id.bt_add_other).setOnClickListener {
//其他消息加
viewModel.otherNum.value = viewModel.otherNum.value!!+1
}
view.findViewById<Button>(R.id.bt_reduce_other).setOnClickListener {
//其他消息減
if (viewModel.otherNum.value!! > 0){
viewModel.otherNum.value = viewModel.otherNum.value!!-1
}
}
view.findViewById<Button>(R.id.bt_clear).setOnClickListener {
viewModel.clear()
}
}
這只是一個簡單的demo,后期可以參照MediatorLiveData對liveData封裝畦浓,MediatorLiveData內(nèi)部維護(hù)一個LiveData列表痹束,我們可以添加一個clear方法,清除其子節(jié)點(diǎn)紅點(diǎn)數(shù)量讶请,這樣只要調(diào)用LiveData.clear即可以清除其所有子節(jié)點(diǎn)紅點(diǎn)祷嘶。