再谈UGUI列表

再谈UGUI列表

  在之前的两篇博客《UGUI的列表》和《UGUI列表进阶》中讨论了如何基于UGUI的ScollView通用可滑动组件设计一个功能相对窄化的列表组件方便使用,采用了类似Android系统中的ListView组件设计思想,抽象化的列表,剥离子项生成与列表本身,使用Adapter机制等等。
  两篇博客一共讨论了两种实现方式,非重用的和重用的,两者的最大区别在于生成的子项数目不同,非重用版本会根据列表的总项目数生成对应数量的子项,在遇到大型列表时效率不佳;重用版本会根据列表的显示区域能装下的子项数目生成对应数量的子项,效率固定,但内部实现更加复杂。
  虽然最后都设计出了满足基本需求的列表组件,但是它们的缺点依然明显,甚至有些致命。最大的一个缺点当属“仅支持子项类型唯一的列表”,这表示一个列表自始至终都只能显示一种子项,一旦有多种子项混合的列表需求,那么按照原本的设计几乎就不可能运行。
  因此,列表组件需要再次改进。
  改造的主要思想是明确列表类型,分为“子项类型唯一”和“子项类型不唯一”两种,理论上两种类型可以由同一种方式实现,但鉴于尽可能简化列表对Adapter的设计需求,最终还是采用了分两种类型实现的方式。
  第一个改动是针对原来方案中以实现IUpdateViews接口的方式与Adapter联系的情况,结合作者的博客《自定义UGUI界面抽象框架》和《自定义事件驱动系统》中提到的抽象框架与事件系统,对列表的抽象层次再次细分。
  新的设计关系图大致如下
  再谈UGUI列表_figure_0
  之后从AbstractListView派生不同类型的列表组件即可


非重用版本改造

  非重用版本的列表组件分为两类,子项唯一和子项不唯一

子项唯一的列表组件

  设计方案和以前博客中讨论过的类似,重点代码如下

public class PlainListView : AbstractListView {

    public const string TAG_PLAIN_LIST_VIEW = "PlainListView"; // 组件名称

    protected int overIndex; // 全局索引
    protected int prevCount; // 缓存上一次的项目数量

    protected Dictionary<int, GameObject> objectCache; // 列表项目缓存,改善效率

    // ------ 公用设置变量
    public bool isHorizontal = false; // 设置是否水平布局(默认垂直布局,即普通列表)
    public RectOffset paddingOffset; // 设置项目边距
    public int spacing; // 设置项目间距

    ……

    // 每帧执行,根据更新标志进行更新
    protected override void execute() {
        if (baseAdapter != null && dataUpdateFlag) {
            if (baseAdapter.getCount() > 0) {
                int innerCount = baseAdapter.getCount() / 30 + (baseAdapter.getCount() % 30 == 0 ? 1 : 0); // 计算每帧需要更新的项目数
                innerCount += (innerCount == 0 ? 1 : 0);
                innerUpdate(innerCount);
            } else {
                foreach (GameObject obj in objectCache.Values) {
                    if (obj.activeSelf) {
                        obj.SetActive(false);
                    }
                }
                dataUpdateFlag = false;
            }
        }
    }
    /// <summary>
    /// 内循环更新
    /// </summary>
    /// <param name="innerCount">内循环更新次数</param>
    private void innerUpdate(int innerCount) {
        for (int i = 0; i < innerCount; i++) { // 内循环
            fromCache(overIndex);
            overIndex++;
            if (overIndex >= baseAdapter.getCount()) { // 全部项目更新完成
                dataUpdateFlag = false;
                while (overIndex < prevCount) { // 更新项目少于已有的项目
                    toCache(overIndex++);
                }
                prevCount = baseAdapter.getCount();
                break;
            }
        }
    }
    ……
}

  基本设计理念没有变化,还是使用LayoutGroup来自动排列子项,使用缓存来优化列表内容变更的效率,优缺点也是一样明确,对大型列表效果不佳。

子项不唯一的列表组件

  该版本的设计思想是按照子项类型分别缓存到不同的队列中,因为对于非重用版本的列表而言,子项唯一与否并不直接影响列表本身的展示,因为要使用列表组件的前提已经是子项有LayoutElement组件描述其尺寸了,ContentRoot的宽高是自适应的,因此子项究竟是什么样子与列表本身的实现无关。
  但是子项类型不唯一会影响到缓存机制,因为当子项唯一时采用的缓存机制是将子项与其索引位置绑定在一起,这样的缓存机制简单而且速度快;可这样的机制面对不同的子项类型时将会失效,当列表数据刷新时无法保证在指定索引上的子项类型满足需求,因此缓存机制必须做出改变。
  改变的方法就是使用两个对象区域,一个保存所有正在展示的对象,另一个保存所有被隐藏掉的对象;为了保证机制的正确性,通过代码在Viewport对象下新建一个CacheRoot对象,将所有缓存下来并隐藏的对象都挂到CacheRoot下,保证不会影响ContentRoot中的子项排列。
  而每次发生数据刷新时会清理所有在前台显示的对象并放入缓存,然后才根据新的数据将对应所需的对象取出来放入显示区域中,如果发现缓存中没有可取得的对象了则立刻新建一个。
  在实际设计编写列表组件之前,为了支持这样的子项,首先要有合适的Adapter,因此需要修改BaseAdapter代码

public abstract class BaseAdapter : IAdapter {

    /// <summary>
    /// 列表刷新回调接口引用
    /// </summary>
    protected IUpdateViews listReference;
    /// <summary>
    /// 关联对象的标签字符串
    /// </summary>
    protected string relatedObject;
    /// <summary>
    /// 关联的列表对象名字
    /// </summary>
    protected string listObjectName;
    ……
    ……
    /// <summary>
    /// 通过索引值获取项目类型
    /// </summary>
    /// <param name="index">索引值</param>
    /// <returns>项目类型</returns>
    public virtual int getObjectType(int index) {
        throw new ObjectTypeMismatchException("No Object Type can be used!");
    }
    /// <summary>
    /// 通过项目类型获取尺寸
    /// </summary>
    /// <param name="index">项目类型</param>
    /// <returns>尺寸数值</returns>
    public virtual float getSizeByType(int viewType) {
        throw new NotImplementedException("Cannot get size by item type, no such implementation!");
    }
    /// <summary>
    /// 多种子项类型的列表中必须知道索引对应的子项的尺寸,如果未重写getObjectType则抛出该异常
    /// </summary>
    public class ObjectTypeMismatchException : ApplicationException {
        private string error;
        private Exception innerException;
        //无参数构造函数
        public ObjectTypeMismatchException() {
            error = "Object Type Mismatch! Please check the adapter implementation!";
        }
        //带一个字符串参数的构造函数,作用:当程序员用Exception类获取异常信息而非ObjectTypeMismatchException时把自定义异常信息传递过去
        public ObjectTypeMismatchException(string msg) : base(msg) {
            error = msg;
        }
        //带有一个字符串参数和一个内部异常信息参数的构造函数
        public ObjectTypeMismatchException(
### 回答1: Unity UGUI 列表是一种常见的 UI 控件,用于展示类别、选项或者数据。它通常由一个滚动视图组成,包含一个或多个列表项,每个列表项包含显示内容、图标和按钮。 在 Unity 中,实现列表通常需要以下步骤: 1. 创建一个滚动区域并设置相关参数,如大小、位置、滚动方向等等。 2. 在滚动区域内创建列表项,通常使用一个 prefab 来实现。 3. 让列表项拥有响应鼠标事件的组件,通常是按钮或 Toggle 组件。 4. 定义列表项的数据结构,如包含名称、描述、图标等属性。 5. 将数据与列表项绑定,使得列表项能够根据数据源的变化自动更新。 6. 定义列表项的样式,如字体、颜色、对齐方式等等。 7. 编写列表项的逻辑代码,处理用户点击触发的事件。 总之,Unity UGUI 列表是一种非常常见和实用的 UI 控件,可以用于许多不同的场景,如游戏菜单、设置界面、排行榜等等。有了列表的支持,用户可以更加便捷地浏览和操作大量的信息。 ### 回答2: Unity UGUI列表是一个UI组件,它允许开发者在游戏设计中创建各种不同类型的列表。在列表中,开发者可以显示一系列项目,用于展示各种信息和元素,例如角色列表、任务列表、排行榜等。 这个组件提供了一些控制选项,例如滚动条、滑动速度、自动布局等。开发者可以使用这些选项来控制列表的行为,使其更接近预期的效果。列表还可以支持不同的布局方式,例如网格布局、表格布局、平铺布局等,以便显示不同格式的项目。 Unity UGUI列表还允许开发者自定义项目的外观和交互。每个项目都可以选择不同的背景图片、字体、颜色和图标等。开发者还可以在每个项目上定义按钮事件或其他交互事件,例如打开一个菜单、播放动态内容等。 总的来说,Unity UGUI列表是一个很有用的UI组件,它可以简化游戏设计中的列表创建过程,并提供了灵活的控制和交互选项。开发者可以利用它来创建丰富的游戏UI,为玩家提供更好的交互和用户体验。 ### 回答3: Unity UGUI 列表Unity 引擎中的一个 UI 元素,用于显示和控制列表中的项目。列表通常用于显示大量的数据,如图像、文本或按钮。Unity UGUI 中的列表组件支持选择、滚动和无限滚动功能。列表组件可以通过在游戏对象中添加 Scroll View、Viewport 和 Content 三个组件来实现。Scroll View 是列表的外壳,Viewport 是显示列表的区域,Content 是列表中的项目。在 Content 中,通过使用 Layout Group 和 Content Size Fitter 来控制列表中的项目排列和大小。 Unity UGUI 列表可以和数据绑定一起使用,通过数据绑定可以动态地更新列表中的项目。例如,在游戏中选择一种角色后,可以通过数据绑定将角色属性动态地绑定到列表中的项目,从而更新列表中的显示内容。列表组件还支持使用导航组件来快速地在列表中导航。在导航组件中,可以使用键盘或鼠标来快速地选择列表中的项目。 Unity UGUI 列表是游戏开发中非常重要的一个元素,它可以用来创建各种类型的列表,如任务列表、道具列表等。在使用列表组件时,需要仔细地考虑列表的设计和布局,以便用户可以轻松地使用列表。使用 Unity UGUI 列表可以使游戏界面变得更加直观和易于操作,增强游戏的可玩性和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值