曾经有一个类似这种需求,想在Unity中实现类似Excel表中的一个功能,能在Scene窗口中“新增行”、“可视化配置”、“所见所得”、“单元格合并”等功能。
经过我对UGUI的一些深层次了解以及结合Editor编辑器窗口开发,先看效果图:
我可在编辑器上实时更改相关参数,那么在Scene窗口中实时的显示最新的效果,并且我也可以看到每一个单元格的ID,可根据ID的信息,然后在代码中填充数据等。也可以进行单元格合并等功能。
以下讲解,只讲解思路+核心代码,不提供任何源码。欢迎大家来提问以及意见。
一、思路解析
首先一个表格最基本的功能,那么就是行【Row】和列【Column】、以及单元格【Cell】这些基本的元素。在往下扩展那么就是”单元格的合并“、”新增行列“、”单元格宽高设置“、”填充单元格数据“、”滚动条“等等功能。
在Unity中,表格功能其实很容易实现,只需要定义几个二维数组绑定行和列,然后在UGUI中用几个Image做成的单元格,然后通过代码生成表格、在麻烦点在代码中计算实现单元格的合并,然后在通过代码去新增行、通过二维值信息,填充数据等等。
但是这么做的很麻烦的点就是,代码是你一个人写的,那么就只有你一个人能理解,别人是没办法通用理解的,因为自己在代码中编写了大量的单元格相关的计算,以及填充数据的计算,我的第一个表格版本就是这样子的。
所以我一直在寻找,怎么让一个什么都不懂的人,用这个表格功能,能几分钟就知道表格功能怎么用,并且很方便的用到自己的需求和项目中去,通过Editor编辑器开发,我实现了类似Excel表的基本功能,基本满足表格需求的大部分功能。
作为一个插件开发者,就是要让一个”小白“能第一眼看到这个东西就会用,哪怕他是很厉害的程序员,我们都不希望它在这个功能中去花费无用以及大量的时间去学习如何使用,当然也不排斥想更深了解的程序员。
秉承这个观点,我开发的插件基本都是非常简单的,复杂的都封装好了,想深层次了解的,也会有大量的代码注释和思路介绍方便去学习。希望这个好习惯可以传输到更多的程序员当中……
二、核心脚本解析
1、单元格(Cell)
作为一个表格最基本的组成部分,单元格。要有单元格背景、单元格文字、宽高信息、该单元格在表格的ID(第几行第几列)。
所以在Unity中需要定义如下属性(单元格只讲属性,一些值的设置方法在后面的功能讲解):
1.1、Background背景
Background属性是Image类型,用于设置单元格的背景。定义如下
1.2、Size大小
Size属性则是设置这个单元格的大小,那么在UGUI中,设置控件的大小则需要RectTransform属性,所以只需要将Vector2类型的Size属性与RectTransform属性进行关联就好了。
1.3、TextName文本信息
这个好理解,就是单元格的文本信息。
1.4、Position坐标
Position属性则是这个单元格所属第几行第几列,行列信息因为是整数,所以采用Vector2Int类型。
1.5、一些单元格Cell的方法
2、行【Row】
行【Row】则非常好理解了,主要是单元格集合,以及行的宽高。需要注意的是不需要定义一个列【Column】类,其实当定义行【Row】的时候,其实单元格集合就是列的集合了,每一个单元格元素其实就是第几列了。
在这里需要注意的是Size属性,也就是行的宽高,这个是用来设置单元格的宽高用途的,具体的调用在后续介绍。因为当我们设置某个单元格的高度时,那么这一行的高度应该都设置,同时如果设置单元格的宽度时,那么这所有行的这一列都应该设置宽度。所以这个值的意义就体现出来了。
3、排版
现在我们需要介绍在UGUI中,表格的一些构件布局。
在表格控件中,我们需要关注Content、Row、Cell。
Content(容器)
Content是存储行的集合,它的格式需要设置如下所示:
为什么要设置成这个样子呢?
因为Content是存储行的父物体,也就是说我们可能会实时的新增行,那么我们就需要有这么多的高度是存储这些行,也方便以后做滚动条,只需要在Content的父物体加一个Mask,然后针对Content进行滚动了,所以我们将Content的布局设置成Top-Center。这样当我们调整高度时,坐标是不会出问题的,可以直接设置RectTransform的Height属性。
Row(行)
Row则是存储单元格的父对象,也就是所谓的行。
为什么要将Row设置成这个样子呢?
在生成表格中,第一我们需要根据行的高度去计算下一个行的坐标,我们需要根据单元格的元素,去计算行的宽度。那么如果我们采用默认的Center-Center。也是可以实现的,那么在计算行和列的时候,就需要多去计算很多的不必要值。
比如:你想知道这一列,最左边的坐标值,那么你需要将Width/2,然后因为位于X轴的坐标,所以这一行最左部的坐标为-Width/2。在计算下一列的时候,你要需要添加一些偏差值,所以在计算和维护中,就会非常的不方便。
这也是为什么将Row的Anchors设置为以上所示。
同时,Pivot属性也是需要设置的,将它设置为(0,1),也就是位于左上角(这一块我是通过代码设置的)。
那么设置完毕后,在计算坐标排版时,如图所示:
第二行的Y轴坐标时-54,其中为什么多出4,是因为行与行之间,定义了一个间隔属性。那么在进行下一行的时候,就会根据Row的高度以及间隔去计算下一行的坐标。
Cell(单元格)
同理,单元格的排版也是跟行类似的,这里就不详细的介绍了,至于为什么这么处理,大家可以详细的看下Row的说明。
4、表格功能【Table】
4.1、表格属性
指定行以及单元格的预制物体。
定义行列、行宽高、以及单元格之间的间隔。
4.2、生成表格
代码设置Content容器的一些描点参数,然后根据行与列,计算Content容器的宽高(设置描点参数,也可以在Scene上直接设置,但是建议能用代码设置的,就代码设置,这样会更直观一些)。
遍历行,并且创建设置行的参数以及进行坐标的计算,当把UGUI的描点弄清楚之后,其实排版的问题是非常简单的。
至于脚本中出现的KGUI_TableRow.cs则是用于存储单元格数据的。
每遍历一行,相应的在这行中,添加列。同理,生成列的时候,也计算出每一列的坐标,以及相关描点、Size、坐标、以及这个单元格的ID(所属第几行第几列)等
在此,表格的创建就完毕了,是不是非常简单,这个时候你就可以在代码中,调用你的方法,则会生成一个表格出来了,但是我们的目标还不够。
4.3、删除表格
删除表格则非常简单了,直接把Rows(创建表格时,每创建一行就会添加到集合中)进行遍历,然后删除掉即可。
这里为什么要用DestroyImmediate()函数,原因是这些创建表格和删除表格都是在编辑器上进行的,而不是运行后执行的。
4.4 填充单元格数据
当表格创建完毕后,那么我们就需要去给每一个单元格填充文本信息了,这就需要在单元格Cell类中,有这么一个可以填充数据的方法。
填充数据非常简单,只需要找到第几行第几列,也就是单元格的ID。
这一块没什么好说的了,也就是获取单元格,然后调用单元格的设置文本方法,将文本信息传输即可。
同理,清空数据就更加简单了,这里就不需要解释了。
5、单元格合并
单元格的合并,则有点复杂了,它是这么一个思路。
5.1、合并列,那么需要知道是第几行中的起始列(startCloumn)~终止列(endCloumn),同时还需要注意的是,如果这一行中,起始列到终止列的中间,以及存在列的合并了,那么这个因素也是要考虑的。
在需要合并的单元格,都加入到起始单元格(StartCell)中,然后将需要合并的其他单元格则进行隐藏,设置StartCell单元格的大小,而这个合并后的单元格的ID就是StartCell的ID。
在Scene窗口和Editor编辑器下就可以实时的看到此时表格的样式情况。以上就是单元格列的合并,当然只支持一行的合并,如果想支持多行多列的合并,那么就需要仔细研究Excel表的合并规则了。
5.2、合并行,同理跟合并列原理是一样的,设置需要合并的列(cloumn),以及设置起始行(startRow)和终止行(endRow)。不过需要注意的是,如果需要合并的这列中,以及存在列的合并了,那么是不允许的,因为这一列以及和其他列进行合并列了,那么我们在进行对这列进行行的合并时,跨度会非常大,处理也会相对复杂,而且在Excel表中也是不允许的,那么我们这里也不允许,因为当表格最终合并复杂话后,处理会相对来说要复杂一些。
实现效果如下:
以上就是单元格的合并了。
6、设置宽高
设置宽高,在Excel表中,其实不知道大家有没有发现,当我设置某一个单元格宽度时,这一列的宽度都会进行设置,当我设置单元格的高度时,那么这一行的高度都会进行设置。
基于这个特性,那么我们在UGUI的实现中,也是这么去做的。
6.1、设置列宽
根据单元格的ID,遍历所有的行,然后获取到同一列的所有行,进行宽度设置,并且往后面的列的坐标也要重新计算。
6.2、设置行高
根据单元格的ID,遍历所有的列,然后设置这一行设置,同时这一行下的所有行也要进行设置。
其实我们发现,当知道单元格的ID后,添加行列、设置宽高、合并等功能都是非常简单的。当然坐标的计算也需要去考虑的。只需要设计合理,这些都是非常简单实现的。
三、Editor表格解析
Editor表格编辑器,就是将这些上面的方法通过编辑器设计的按钮来直接调用,并且提供表格的实时样式,来进行查看,还有一些数据显示等。
如果大家对Inspector属性编辑器开发不理解的,可以看我的【UnityEditor-Windows编辑器与Inspector编辑器】文章,里面有详细的介绍。
所以表格的Editor重点时核心代码的相关解释。
1、基本属性布局
2、表格实时样式
根据单元格Cell的隐藏情况,来设置该单元格是否支持点击,然后实时遍历所有行中的单元格,进行控件绘制。
3、选中单元格,详细信息
当选中某一个单元格时,显示这个单元格的基本信息,以及信息的设置更新等。
4、合并单元格
合并单元格则非常简单,直接调用表格的相关方法集合。
5、单元格信息设置
单元格信息设置,也许需要定制单元格的样式、单元格的文字信息等,那么就需要在这里进行设置了。
其中UpdateCell,则是遍历所有单元格,进行单元格信息更新,这里不需要过多解释。