zoukankan      html  css  js  c++  java
  • Unity应用架构设计(6)——设计动态数据集合ObservableList

    什么是 『动态数据集合』 ?简而言之,就是当集合添加、删除项目或者重置时,能提供一种通知机制,告诉UI动态更新界面。有经验的程序员脑海里迸出的第一个词就是 ObservableCollection。没错,它在WPF中盛行其道,通过它开发者可以很方便的达到动态更新界面。要在Unity 3D中使用ObservableCollection还是有些许困难的,因为Mono并不提供ObservableCollection类。但实际上,自己动手去构建一个『动态数据集合』也非难事,核心在于怎样去传播通知。这也是本篇博客的主题。

    实现自定义的ObservableList

    既然核心在于构建通知机制,谈到『通知』两字,最常见的形式就是以委托或者事件形式将消息广播给监听者。遗憾的是,.NET中常见的集合数据结构List并不支持事件的通知。所以我在自定义的ObservableList中增加OnAdd,OnRemove,OnInsert事件,当集合添加或者删除项时,能广播通知给客户端UI界面。

    以下图为例,当点击+时,『以数据驱动界面的形式』,动态的去更新UI界面:

    既然要以数据来驱动界面,首先我们需要定义能存放数据的集合,它就是ObservableList,并且是实现了IList 接口:

    public class ObservableList<T>:IList<T>
    {
        //...省略部分代码...
        private List<T> _value=new List<T>();
    
        public delegate void AddHandler(T instance);
        public AddHandler OnAdd;
    
        public delegate void InsertHandler(int index,T instance);
        public InsertHandler OnInsert;
    
        public delegate void RemoveHandler(T instance);
        public RemoveHandler OnRemove;
    
        public void Add(T item)
        {
            _value.Add(item);
            if (OnAdd != null)
            {
                OnAdd(item);
            }
        }
    
        public bool Remove(T item)
        {
            if (_value.Remove(item))
            {
                if (OnRemove != null)
                {
                    OnRemove(item);
                }
                return true;
            }
            return false;
        }
    
        public void Insert(int index, T item)
        {
            _value.Insert(index,item);
            if (OnInsert!=null)
            {
                OnInsert(index, item);
            }
        }
    
    }
    

    可以看到,自定义的ObservableList实现了 IList 接口,并以泛型的形式约束了数据项类型。当添加或者删除项时,提供了以事件的形式告诉客户端UI界面 ,作为观察者的UI可以顺势做出相应的更新。

    岔开话题说一下,为什么要用泛型,这是几天前有同学在群里问的?

    • 好处1:可以约束数据项的类型,让我们不用每时每刻去强转。比如你往ArrayList中添加了若干数据,因为ArrayList的数据项Item是万能的object,所以你每次取出来都需要将object转为你想要的对象,麻烦。
    • 好处2:减少运行时错误,因为是数据项是object,所以在编译时你可以将其强转为任何类型,但万一这个object实际是Datetime类型,但你强转为int,编译时是没问题的,但一运行就报错,泛型约束能有效减少这种情况

    完善ObservableList

    到目前为止,我们自定义的动态数据集合ObservableList是非常好的设计,但唯一不足的事,它不能支持初始化时通知UI界面更新。 『初始化』 这词可能有点太术语了,我翻译一下就是一般初始化一个List,我们都是像如下方式进行:

    public ObservableList<FaceBox> DataSource = new ObservableList<FaceBox>
    {
        new FaceBox
        {
            Name = "Eyes",
            Level = 10,
            Face = "Avatar201_Face",
            Badge = new Badge {Icon = "Icon_WeaponRod", ElementColor = "1CB9FFFF"}
        },
        new FaceBox
        {
            Name = "Jack",
            Level = 8,
            Face = "Avatar202_Face",
            Badge = new Badge {Icon = "Icon_WeaponSpear", ElementColor = "FF5821FF"}
        }
    }; 
    

    显然这即没有触发OnAdd,也没有触发OnRemove等事件,那么初始化或者重置列表时,UI界面还是得不到更新。那我们怎么去解决呢?还记得第一章中BindableProperty吗?对了,解决方案就是它,对列表的初始化或者重置就是对Value进行改变。而BindableProperty内部提供了对Value值改变的监听,一旦Value改变了,将消息广播出去。

    OK,我们增强一下ObservableList:

    public class ObservableList<T>:IList<T>
    {
    	//省略部分代码...
        public delegate void ValueChangedHandler(List<T> oldValue, List<T> newValue);
        public ValueChangedHandler OnValueChanged;
       
        //预先初始化,内置的List,防止空异常
        private List<T> _value=new List<T>();
        public List<T> Value
        {
            get { return _value; }
            set
            {
                if (!Equals(_value, value))
                {
                    var old = _value;
                    _value = value;
    
                    ValueChanged(old, _value);
                }
            }
        }
    
    
        private void ValueChanged(List<T> oldValue, List<T> newValue)
        {
            if (OnValueChanged != null)
            {
                OnValueChanged(oldValue, newValue);
            }
        }
    
    }
    

    所以客户端UI界面只要对ObservableList的OnValueChanged事件进行监听,当初始化或者重置时,你也可以得到更新,演示效果如下:

    小结

    自定义的动态数据集合ObservableList看起来小巧,但五脏俱全,能提供通知机制,可以动态的去更新UI界面。 所有的一切都以数据的改变来驱动UI,这是非常重要的转变。所以看似代码复杂了,但实际上你只要关心数据即可。
    源代码托管在Github上,点击此了解

  • 相关阅读:
    什么是子网掩码?(转)
    测试LM414-IOT网关MQTT功能
    连接s7-200时,提示未找到指定的访问点
    github中文件夹后面跟@+数字什么意思?为什么git clone下来里面是空的?
    Java知识32 数据结构 枚举 向量【多测师】
    java知识31 void 、实例化对象后面带参数、 实例变量(重点)【多测师】
    Java知识30 package【多测师】
    Java知识29 接口【多测师】
    java知识28 Java封装【多测师】
    Java知识27 抽象类【多测师】
  • 原文地址:https://www.cnblogs.com/OceanEyes/p/setting_up_observablelist.html
Copyright © 2011-2022 走看看