我们任何的信息化建设项目都为分析信息服务的,信息的载体无非就是各种各样的数据,我们表现和收集数据用得最多的应该就是表格了,最近一个项目中大量使用到表格,而且对表格的自定义要求甚高,一开始我自恃JS功力深厚,用JS给整了一套自定义表格组件,能实现大部分表格自定义功能要求,可是客户的要求是越来越高,而我又找不到拒绝的理由,这时JS做的自定义表格控件已经不能满足更高的要求了,面对几千行的JS代码(对JS的OO不怎么会,都用的OP,现在看那代码真烂,还高手)已经无从下手,再加上各浏览器产商标准的不统一,使JS的兼容性大打折扣,就连盖茨兄家的浏览器都一个版本一个样,真是害苦了我们这群兄弟,虽然网上各种开源非开源JS类库比比皆是,但要能都兼容的还真没有找着;
为了解决剩下的JS不能或非常难解决的问题,我不得不另外寻找出路,为提高用户的体验性和操作友好性,我对比了主流的富客户端框架JavaFX、Flex、Silverlight,JavaFX其实我是比较看好的,但是他现在的版本组件太少,开发成本很高,而且现在也没有开放源代码;Silverlight是盖茨兄家的作品,但他非常霸道,对windows外的系统支持并不是很好,最终我选择了出道比较久的Flex,FlashPlayer插件的安装率也是最高的,兼容性方面可以不用太多考虑;虽然正在酝酿中的HTML5功能非常强大,而且也是属于业内标准,但在5-8年内应该很难在实际应用中得到使用;但看了后发现要真正在实际项目中使用还有相当一段路要走。Flex现在版本是Flex4,虽然以前有用的Flex3,但也只是做些图表时用,真正的复杂应用并没有什么接触,这次正好想练练手;
想法往往是美好的,现实始终是残酷的,在开发过程中不断的遇到问题,一个是自己本身对Flex4的不了解,二一个是Flex4刚出来不久,各种帮助资源都少,参考Flex3发现很大的变化,还好Adobe公司的帮助做的好,慢慢查基本能解决。
切入正题,问题一:表格的单元格合并拆分,参考Excel的做法;
看似很简单的单元格合并、拆分,但实际操作过程中却发现并不好做,一开始我按原来JS实现思路来做,但发现不行,效果达不到像Excel那样的任意跨行跨列的合并、拆分;自能另找方案,仔细分析Excel中单元格的合并只能是一个矩形,我想只要我能解决并判断出我选择的单元格是一个矩形就可以解决了,首先我需要选择待合并单元格,这个比较简单,通过监听鼠标按下并移动事件就能在单元格上方绘出一块透明矩形层,然后我用该层测试与各单元格的碰撞得到碰撞到的单元格对象放入集合中备用;其实这是我得到的所有单元格就已经是一个矩形了,但我还是需要重新计算,因为我还实现了按住Ctrl或Shift功能键选择单元格功能,这是我就不能确定是否是矩形了,如何计算呢?其实很简单,只要得出最左上角单元格的行号、列号和最右下角的行号、列号,即最大、最小行号、列号;然后通过(maxRow-minRow+1)*(maxCol-minCol+1)是否等于我们选中单元格总数;(其实就是很普通的矩形面积计算公式,但是一开始真不知道如何去做,我一直在想Excel中是怎样的一种算法来实现的,有知道的朋友可以给我留言,谢谢!)合并过程中如果遇到其中有已合并单元格的还需要考虑其合并后的colSpan和rowSpan;这样合并功能就不难实现了;但接着问题又来了,也是我们已经考虑到的colSpan和rowSpan,在实际实现过程中我们并不太容易计算出各种合并情况,当我们在合并的单元格中存在已合并单元格时就会出问题,这个我想了很久都不知道如何去计算得出正确的合并,最后我选择了先将合并的单元格拆分成原始单元格再合并,这样看似增加了程序的重复工作,但我觉得相比复杂的计算,在小范围内合并效率应该不低;当然在实际代码实现中我们还需要判断选中的单元格是不是连续的;这些都做好了,那我们的程序应该是没问题了;
部分关键代码:
// 合并单元格
public function cellCoalition(target:EgExcelGrid, cells:ArrayCollection):void
{
if(cells!=null && cells.length>1)
{
var cellArray:ArrayCollection = new ArrayCollection();
// 先分拆存在合并的单元格
for(var n:int=0;n<cells.length;n++)
{
var rearray:ArrayCollection = cellSplit(target, cells[n], true);
if(rearray!=null && rearray.length>0)
{
cellArray.addAll(rearray);
rearray.removeAll();
}
rearray = null;
}
if(cellArray!=null && cellArray.length>0)
{
cells.addAll(cellArray);
cellArray.removeAll();
}
cellArray = null;
var maxRow:int = 1; // 最大行
var minRow:int = 999999999; // 最小行
var maxCol:int = 1; // 最大列
var minCol:int = 999999999; // 最小列
var cellsNum:int = 0;
var minCell:EgTextItem; // 最左上角cell
var minnum:int;
var _cell:EgTextItem;
// 最终要合并的单元格总数
cellsNum = cells.length;
for(var i:int=0;i<cellsNum;i++)
{
_cell = cells.getItemAt(i) as EgTextItem;
var _minnum:int = int(_cell.colSpan.toString() + _cell.rowSpan.toString());
// 最大最小行
if(_cell.cellPropertyes.rownum<minRow)
{
minRow = _cell.cellPropertyes.rownum;
}
else if(_cell.cellPropertyes.rownum>maxRow)
{
maxRow = _cell.cellPropertyes.rownum;
}
// 最大最小列
if(_cell.cellPropertyes.colnum<minCol)
{
minCol = _cell.cellPropertyes.colnum;
}
else if(_cell.cellPropertyes.colnum>maxCol)
{
maxCol = _cell.cellPropertyes.colnum;
}
// 最小单元格对象
if(minCell==null || _minnum<minnum)
{
minnum = _minnum;
minCell = _cell;
}
}
// 得出矩形可以合并
if(cellsNum==(maxRow-minRow+1)*(maxCol-minCol+1))
{
// 删除除最左上角cell外其他cell
for(var j:int=0;j<cells.length;j++)
{
if(minCell!=cells[j])
{
for(var r:int=minRow;r<=maxRow;r++)
{
var rowobj:GridRow = target.getChildAt(r) as GridRow;
if(rowobj.getChildren().indexOf(cells[j])>-1)
{
rowobj.removeChild(cells[j]);
}
}
}
}
minCell.colSpan = minCell.colSpan + maxCol - minCol;
minCell.rowSpan = minCell.rowSpan + maxRow - minRow;
}
else
{
trace("不理睬");
}
cells.removeAll();
cells = null;
target = null;
minCell = null;
}
}
问题二:冻结表头和列,由于项目的一些特殊要求,我并没有找到合适的表格组件;Flex4自带的DataGrid组件以及他的高级表格组件都不能满足我的要求;网上另一个MecGrid组件倒是与我的要求有点接近了,本想改造下来实现我的需求,但是他却不是开源的,无奈我只好自己通过继承Grid、GridRow、GridItem来实现。
这个问题我还没有找到合适的解决方案,我在冻结他们时,发现他们被移动的主体部分遮住了,我尝试使用setItemAt(n)来更改他的顺序,这样他不会被覆盖,但发现在滚动条滚动时并不容易控制它的位置;希望能有高手给点提示;