基于LiveData实现小红点系统

前言

小红点提示,可以说是现在app都会有的一个功能,比如微信的消息界面,微博的我的界面。一般来说,一个小红点不会单独存在,可能会和其他红点之间有所关联,构成红点系统。


image.png

红点系统可以看做是树形结构,红点系统设计遵循以下原则:

  • 子节点上的红点变化,需要通知其父节点更新
  • 父节点上红点数量,是其所有子节点红点数量之和
  • 父节点上红点清除,要将其所有子节点红点清除(数量置为0)

之前我们小红点是这么处理的

当收到红点消息时,通过EventBus方式通知对应的红点view更新,同时需要发送event通知其父节点,父节点收到通知需要计算它所有子节点红点数量之和,然后再更新自身,如果该父节点还有父节点,需要再发送一个event......
可以看出,这是一个非常糟糕的设计,可以说毫无设计。这在红点数量少的时候,感觉不到问题,但是随着版本不断迭代,需要的红点越来越多,就会发现它是多么的糟糕。代码中存在大量的event来处理红点,收到一个红点消息,可能要发好几个event,代码逻辑混乱,增加删除红点复杂,如果不熟悉业务逻辑,很容易漏掉相关代码。

后来我是这么设计的

基于观察者模式,设计下面两个类

1.红点处理类
  • 维护自身的key,父节点key,子节点key(如果有的话)
  • 处理tcp推过来的消息,通知观察者更新视图,并通知父节点更新
  • 清除红点消息,并通知父节点更新,同时清除子节点红点
2.红点中心类
  • 注册红点处理类
  • 注册观察者(向红点处理类注册观察者)
  • 接收tcp发送的红点信息,然后分发给对应红点处理类
  • 接收清除红点信息,然后分发给对应的红点处理类
image.png

这样设计的好处是,低耦合,代码逻辑清晰,增加删除红点方便,符合数据驱动设计思想,每次有红点消息变化时,只要更新对应的红点数据就可以了。

LiveData

后来接触了LiveData,觉得代码可以更简单一点,LiveData本身就是观察者模式,还有生命周期处理等其他好处,用它来处理红点应该会很合适。每个红点对应一个LiveData,如果该红点是一个父节点,则使用MediatorLiveData 官网上是这么描述的

MediatorLiveDataLiveData 的子类,允许您合并多个 LiveData 源。只要任何原始的 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者。
这样我们可以写一个全局的ViewModel,里面存放这些红点对应的LiveData,红点view所在的界面,observer这些LiveData,当收到红点消息时,直接LiveData.setValue就行,这样代码逻辑上会很简单。
写了一个小demo

1636961074445984.gif
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 {
            //系统消息加
            viewModel.systemNum.value = viewModel.systemNum.value!!+1
        }

        view.findViewById<Button>(R.id.bt_reduce_system).setOnClickListener {
            //系统消息减
            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内部维护一个LiveData列表,我们可以添加一个clear方法,清除其子节点红点数量,这样只要调用LiveData.clear即可以清除其所有子节点红点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容