跳至主要內容

Umg6.ListView

Mr.Si大约 6 分钟unreal

导读

头像
少年,你是否在为显示大量数据在列表上而苦苦烦闹?知道有ListView这个东西却不知道怎么用?

ListView & TileView

头像
本质上来说TileView是ListView的一个扩展
UTileView : public UListView
头像
创建C++类的列表中也能看到
头像
意味着我们可以利用多态来无痛切换ListView和TileView
头像
不明觉厉!

基本流程

1. 新建一个UMG,作为ListView载体

头像
ListView我们可以理解成一个负责展示数据的容器。
头像
额!为什么不能像滚动框一样加入内容?
头像
这就要说说他和滚动框的区别了
功能对比ListView(TileView) 🏆ScrollBox 📜
数据管理绑定 TArray<UObject*>自动复用 UI 项直接添加 UWidget,手动管理 UI
性能优化仅渲染可见项,适合大数据量,优化性能所有项一次性创建,数据多时可能卡顿
适用数据结构化数据列表(如 TArray<UObject*>少量 UI 动态元素,如富文本、单项内容
交互支持内置 OnItemClickedSelection 等事件需手动绑定 ButtonScroll 事件
子项布局ListView(纵向),TileView(网格)手动布局子项(如 VerticalBoxHorizontalBox
刷新方式RequestRefresh()RefreshItem()直接移除/添加 Widget
适用场景长列表、多数据项,如角色选择、商店物品、技能列表等小范围的动态 UI 元素,如聊天记录、日志
UI 管理高效管理大量动态数据,自动更新和回收 UI 项手动管理每个 Widget,适合静态或少量数据
内存占用优化内存,避免重复创建项,节省资源可能占用更多内存,所有项都要存在 UI 树中
头像
ScrollBox这有多少数据就新建多少UI条目,而ListView存储着你的数据,但UI条目只会显示看得到的对吧?
头像
是的,ListView只会渲染可见的子项UMG

2. 配置子项使用的条目控件类

头像
条目也是一个UMG,只不过里面带有一些接口。
头像
然后呢?我打开游戏后没有反应啊!
头像
那是你没有添加条目进去!

3. 创建条目UMG

头像
我加了啊!我创建了对应的UMG,添加进去后没有反应啊!
头像
NONONO!这里需要创建UObject,而不是子项UMG
头像
一脸懵逼

4. 创建数据类UObject

头像
ListView使用了类似MVC的思想,将数据、视图、控制器分离,意味着我们需要手动创建一个数据类来作为数据载体。

方便测试里面加了名称和图标两个变量

头像
难不成要新建UObject 然后添加到子项中?
头像
是的,这样屏幕上就可以显示出对应的子项了!不过在这之前你需要在子项UMG的接口中将数据源Cast成你的数据类,以便解析数据。
头像
给按钮点击回调上,为了更加直观我们用了一个自增整数作为索引

其他细则

头像
如何验证他只是绘制可见项呢?
头像
很简单,给他加一个Check组件,请记住我们现在最多显示4个条目,意味着第五个要拉滚动框才能看到。
头像
!我勾选第一个的Check,后面的也被勾选了!
GIF

如何修改数据?

头像
那岂不是这个勾选框没用了!
头像
年轻人!你似乎没有Get到这个点,为了让你更好的理解这一点,我用一个图来解释这一个过程。

时序图

头像
我懂了!好比告诉ListView组件我要用这个UMG类的模板生成子项,生成后将对应的数据用接口传递过去!
头像
是的正因为这种机制,你的CheckBox其实被认定为View层面的东西,他并不涉及数据的修改,所以视觉上拉下滚动框会出现错误的勾选选项。

刷新方法 RegenerateAllEntries

头像
所以正确的方法是存储check状态,后续生成时重新赋值checkbox,同时checkbox勾选时设置对应的变量。
头像
本质其实就是UI和我们的数据做了一次手动绑定对吧!虽然重复的问题解决了,如果我想勾选当前项其他的勾选框复位怎么做呢?
头像
从操作上来说,你应该获取当前的勾选的选项,并且将其他选项中的 勾选框复原。
头像
意味着我们不应该在UI中修改我们的数据,并将CheckBox的命中关闭
头像
在ListView层去监听选项变化。
头像
额,这么做好像要遍历所有的数据,我觉得可以缓存一个临时变量存储上一次选中的数据和当前选项作比较。
头像
其实还有一种方法,官方已经给了你接口,这种也属于纯UI表现,非常适合做UI的选定样式,但数据上没有缓存UI状态。
GIF

RegenerateAllEntries && RequestRefresh 区别?

头像
RegenerateAllEntries顾名思义,属性当前视图显示的所有UI,而RequestRefresh则反直觉, 是刷新未显示的部分数据,不会直接刷新当前显示的数据。