基于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(0)
    val friendNum = MutableLiveData(0)
    val otherNum = MutableLiveData(0)

    val totalNum = MediatorLiveData()

    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(R.id.tv_message_system).text = it.toString()
        }

        viewModel.friendNum.observe(viewLifecycleOwner){
            view.findViewById(R.id.tv_message_friend).text = it.toString()
        }

        viewModel.otherNum.observe(viewLifecycleOwner){
            view.findViewById(R.id.tv_message_other).text = it.toString()
        }

        viewModel.totalNum.observe(viewLifecycleOwner){
            view.findViewById(R.id.tv_message_all).text = it.toString()
        }

        view.findViewById

这只是一个简单的demo,后期可以参照MediatorLiveData对liveData封装,MediatorLiveData内部维护一个LiveData列表,我们可以添加一个clear方法,清除其子节点红点数量,这样只要调用LiveData.clear即可以清除其所有子节点红点。

你可能感兴趣的:(基于LiveData实现小红点系统)