TSTableView是Tacticsoft工作室开发的一款适用于UGUI的列表(Table)插件,设计灵感来源于iOS/Mac的UITableView,提供高复用、高性能的列表,其主要特点是:
采用MVC模式
良好的性能和内存占用优化
复用列表项
遗憾的是它暂时不支持横向列表,需要开发者自己扩展,还有就是优化无止境。
首先介绍下TSTableView的使用方法。
上图是TSTableView编辑时的层次结构。TableViewContainer节点挂载Mask(UGUI)组件,同ScrollView(UGUI)一样用于列表显示区域的裁剪。TableView节点挂载了TableView组件和ScrollRect(UGUI)组件。TableViewContent节点挂载了VerticalLayoutGroup组件,用于列表项的纵向布局。
TSTableView设计上采用MVC模式,View功能由TableView提供,控制显示和处理用户输入;实现ITableViewDataSource接口的类实现Model功能,提供列表数据;自定义一个Controller组件来控制视图和通知状态改变,为TableView设置数据源,需要将它挂载到具体对象上。
其中ITableViewDataSource定义如下,它为TableView提供列表项数量、列表项高度、创建列表项接口:
public interface ITableViewDataSource
{
// get the number of rows that a certain table should display
int GetNumberOfRowsForTableView(TableView tableView);
// get the height of a row of a certain cell in the table view
float GetHeightForRowInTableView(TableView tableView, int row);
// create a cell for a certain row in a table view,
// callers should use tableView.GetReusableCell to cache objects.
TableViewCell GetCellForRowInTableView(TableView tableView, int row);
}
除此之外,需要提供列表项预制件,它设计了具体的列表显示,需要挂载TableViewCell子类组件和LayoutElement组件,以便TableView能够识别和调用相关接口。
以下是TSTableView运行时的层次结构:
TableViewContent节点下存放所有正在使用的列表项,其中TopContentPlaceHolder和BottomContentHolder提供占位功能,缓存的列表项放在隐藏的ReusableCell节点下。
接下来重点讨论TableView的设计原理。
TableView维护一个可见列表项字典和一个缓存列表项字典,可见列表项使用字典(Dictionary)相比列表(List)会占用更多内存空间,但查找、删除效率更高,其实可以考虑使用链表(LinkedList)可能更好一些。缓存列表因为需要支持不同列表项的混排,采用按标识字符串做key的字典,value使用链表来串起同类列表项。
private Dictionary<int, TableViewCell> m_visibleCells;
private Dictionary<string, LinkedList<TableViewCell>> m_reusableCells;
TableView层次结构上非常巧妙地设计两个占位项来动态计算和伸缩占位空间,分别是TopContentPlaceHolder和BottomContentPlaceHolder,它们挂载LayoutElement组件,能够动态改变占位空间。
假定当前使用的是一个超长列表。初始状态下,不需要TopContentPlaceHolder占位隐藏即可,除了视口可见的几个列表项,其余列表项因为不可见无需在列表中存在,而内容空间空间都由BottomContentPlaceHolder来完成,此时无缓存项。上面是层次结构和布局示意图。
随着列表滑动到中间位置,TopContentPlaceHolder和BottomContentPlaceHolder同时可见,它们分别代表视口之上的不可见空间和视口之下的不可见空间。视口中可显示的列表项数目不预知,因为每个列表项的高度可变,列表项可以是不同类型,所以列表在不同滑动位置,缓存字典中的缓存项数目会动态变化。上面是层次结构和布局示意图。
当列表项滑动到底部时,底部不需要占位项BottomContentPlaceHolder,顶部占位项TopContentPlaceHolder则会占取视口之外的内容空间。上面是层次结构和布局示意图。
为了维持可见列表和计算当前上下占位空间的大小,TableView维护了两个数组。一个是各列表项高度值数组(包含留白),会随着列表初始化、删除和添加列表项、列表重建,相对应地重建和修改高度值数组。另一个是各列表项累积高度值数组,记录各列表项距顶部的高度值,可以用来快速计算并获取当前视口中可见的列表项。同时使用一个已累积索引变量来记录已计算累积高度值的索引,这样可以在需要的时候才去完成累积值计算,得到惰性计算的目的。
public float[] m_rowHeights;
private float[] m_cumulativeRowHeights;
private int m_cleanCumulativeIndex;
TSTableView是一个针对超长列表的小巧插件,其设计上巧妙地利用现有UGUI组件来达到高复用、高性能的列表效果,可以为我们自定义控件提供一些设计上的方法。
附上:
开源网址: https://bitbucket.org/tacticsoft/tstableview