创建并置表,第 1 部分: 使用 Flex 创建 JTable
2009-11-11 00:00:00 来源:WEB开发网 闂傚倸鍊搁崐鎼佸磹閹间礁纾归柟闂寸绾惧綊鏌熼梻瀵割槮缁炬儳缍婇弻鐔兼⒒鐎靛壊妲紒鐐劤缂嶅﹪寮婚悢鍏尖拻閻庨潧澹婂Σ顔剧磼閻愵剙鍔ょ紓宥咃躬瀵鎮㈤崗灏栨嫽闁诲酣娼ф竟濠偽i鍓х<闁绘劦鍓欓崝銈囩磽瀹ュ拑韬€殿喖顭烽幃銏ゅ礂鐏忔牗瀚介梺璇查叄濞佳勭珶婵犲伣锝夘敊閸撗咃紲闂佺粯鍔﹂崜娆撳礉閵堝洨纾界€广儱鎷戦煬顒傗偓娈垮枛椤兘骞冮姀銈呯閻忓繑鐗楃€氫粙姊虹拠鏌ュ弰婵炰匠鍕彾濠电姴浼i敐澶樻晩闁告挆鍜冪床闂備胶绮崝锕傚礈濞嗘挸绀夐柕鍫濇川绾剧晫鈧箍鍎遍幏鎴︾叕椤掑倵鍋撳▓鍨灈妞ゎ厾鍏橀獮鍐閵堝懐顦ч柣蹇撶箲閻楁鈧矮绮欏铏规嫚閺屻儱寮板┑鐐板尃閸曨厾褰炬繝鐢靛Т娴硷綁鏁愭径妯绘櫓闂佸憡鎸嗛崪鍐簥闂傚倷鑳剁划顖炲礉閿曞倸绀堟繛鍡樻尭缁€澶愭煏閸繃宸濈痪鍓ф櫕閳ь剙绠嶉崕閬嶅箯閹达妇鍙曟い鎺戝€甸崑鎾斥枔閸喗鐏堝銈庡幘閸忔﹢鐛崘顔碱潊闁靛牆鎳愰ˇ褔鏌h箛鎾剁闁绘顨堥埀顒佺煯缁瑥顫忛搹瑙勫珰闁哄被鍎卞鏉库攽閻愭澘灏冮柛鏇ㄥ幘瑜扮偓绻濋悽闈浶㈠ù纭风秮閺佹劖寰勫Ο缁樻珦闂備礁鎲¢幐鍡涘椽閸愵亜绨ラ梻鍌氬€峰ù鍥敋閺嶎厼鍨傞幖娣妼缁€鍐煥濠靛棙顥滈柣锕備憾濮婂宕掑▎鎺戝帯濡炪們鍨归敃銈夊煝瀹ュ鍗抽柕蹇曞Х椤斿姊洪幖鐐插姶闁告挻鐟╅幃姗€骞庨懞銉у幐闂佸憡鍔戦崝搴㈡櫠閺囩姷纾奸柍褜鍓熷畷姗€鍩炴径鍝ョ泿闂傚⿴鍋勫ú銈吤归悜鍓垮洭鏁冮埀顒勬箒濠电姴锕ら悧蹇涙偩濞差亝鐓涢悘鐐额嚙婵″ジ鏌嶇憴鍕伌鐎规洖宕埢搴ょ疀閹惧妲楃紓鍌氬€搁崐鐑芥⒔瀹ュ绀夐幖杈剧到閸ㄦ繃銇勯弽顐粶濡楀懘姊洪崨濠冨闁搞劍澹嗙划濠氬箮閼恒儱鈧敻鏌ㄥ┑鍡欏嚬缂併劏妫勯湁闁绘ǹ宕甸悾鐑樻叏婵犲啯銇濇俊顐㈠暙閳藉鈻庨幇顓炩偓鐑芥⒑鐠囨彃顒㈤柣顓у櫍瀹曪繝骞庨懞銉ヤ粧濡炪倖娲嶉崑鎾垛偓瑙勬礀閻栧ジ銆佸Δ浣哥窞閻庯綆鍋呴悵顐⑩攽閻樻剚鍟忛柛锝庡灣瀵板﹪宕滆閸嬫挾绮☉妯绘悙缂佺姵鐓¢弻娑㈠Ψ椤旂厧顫╅梺钘夊暟閸犳牠寮婚敐澶婃闁圭ǹ瀛╅崰鎰版⒑閼姐倕鏋庣紓宥咃躬瀵鈽夐埗鈹惧亾閿曞倸绠f繝闈涙川娴滎亝淇婇悙顏勨偓銈夊礈濞嗘挻鍋嬮柛鈩冪▓閳ь剚妫冨畷姗€顢欓崲澹洤绠圭紒顔煎帨閸嬫捇鎳犻鈧崵顒傜磽閸屾艾鈧娆㈤敓鐘茬獥婵°倕鎳庣粻浼存煙闂傚鍔嶉柛瀣ф櫊閺岋綁骞嬮敐鍡╂缂佺虎鍘搁崑鎾绘⒒娴h櫣甯涢柛鏃€娲滅划鏃堟濞磋櫕鐩畷姗€顢欓崗鍏夹氶梻渚€鈧偛鑻晶顖炴煏閸パ冾伃妤犵偞甯¢獮瀣攽閹邦亞纾婚梺璇叉唉椤骞愭搴g焼濞撴埃鍋撻柛鈺冨仱楠炲鏁傞挊澶夋睏闂備礁婀辩划顖滄暜閳哄倸顕遍柍褜鍓涚槐鎾存媴閻熸澘濮㈤悷婊勫閸嬬喖宕氶幒鎴旀瀻闁规儳鐤囬幗鏇炩攽閻愭潙鐏﹂柣顓у枛閳讳粙顢旈崼鐔哄幍闁荤喐鐟ョ€氼剚鎱ㄩ崶銊d簻闁靛濡囩粻鐐存叏婵犲啯銇濋柡灞芥嚇閹瑩鎳犵捄渚純濠电姭鎷冮崒姘ギ闂佸搫鐬奸崰鏍箹瑜版帩鏁冮柨婵嗘噽閿涙捇姊绘担鐟邦嚋缂佽瀚板畷鎴濃槈閵忕姷鍘撮梺鐟邦嚟婵參宕戦幘缁樻櫜閹煎瓨锚娴滅偓銇勯幘瀵糕姇婵炲懎锕弻锛勪沪閻e睗锝嗙箾绾板彉閭鐐茬箳娴狅箓鎸婃径濠呭帿闂傚倸鍊烽悞锕傛儑瑜版帒纾归柡鍥ュ灩缁犵娀鏌熼柇锕€鏋熸い顐f礋閺岀喖骞嗚閹界姴鈹戦娑欏唉闁哄本鐩獮姗€寮堕幋鐘点偡闂備礁鎲¢幐绋跨暦椤掑嫧鈧棃宕橀鍢壯囨煕閳╁喚娈樺ù鐘虫倐濮婃椽鎳¢妶鍛瘣闂佸搫鎳忛惄顖炲箖妤e啯鍊婚柦妯猴級閵娧勫枑濠㈣埖鍔曠壕濠氭煙閸撗呭笡闁哄懏鐓¢獮鏍垝閻熸澘鈷夐梺璇茬箰缁夌懓顫忛搹鍦<婵☆垵顕ч棄宥呪攽閻愭彃绾ч柨鏇樺灪娣囧﹪鎮界粙璺槹濡炪倖鐗楀銊╂偪閳ь剟姊婚崒姘偓鎼佹偋婵犲嫮鐭欓柟閭﹀枦婵娊鏌ゅù瀣珖缁炬崘妫勯湁闁挎繂鐗婇ˉ澶愭煟閹炬潙濮堥柟渚垮妼铻g紒瀣仢椤鈹戦垾鍐茬骇闁告梹鐟╅悰顔嘉熼崗鐓庣彴闂佽偐鈷堥崜锕€危娴煎瓨鈷掑ù锝嚽归弳閬嶆煙绾板崬浜扮€规洘鍔栫换婵喰掔粙鎸庡枠鐎殿喛鍩栭幆鏃堝箻鐎涙ɑ婢戝┑锛勫亼閸婃牕顫忔繝姘ラ悗锝庝憾閸熷懘鏌曟径娑滅濞存粍绮嶉妵鍕箻鐠鸿桨绮跺┑鈩冨絻椤兘寮婚敐澶嬫櫜闁搞儜鍐ㄧ婵°倗濮烽崑鐐垫暜閿熺姷宓侀悗锝庡枛缁秹鏌嶈閸撶喖骞冨Δ浣虹瘈婵﹩鍘搁幏娲煟閻斿摜鎳冮悗姘煎弮瀹曟洖螖閸涱喚鍘卞┑鈽嗗灥閵嗏偓闁稿鎹囬幃銏ゅ箵閹烘垹闃€婵犵數濮烽弫鍛婃叏閻戣棄鏋侀柛娑橈攻閸欏繘鏌i幋锝嗩棄闁哄绶氶弻娑樷槈濮楀牊鏁鹃梺鍛婄懃缁绘垿濡甸崟顖氱闁告鍋熸禒鑲╃磼閻愵剙鍔ゆい顓犲厴瀵鎮㈤悡搴n槶閻熸粌绻掗弫顔尖槈閵忥紕鍘撻梻浣哥仢椤戝懘鎮橀敃鍌涚厪闁搞儜鍐句純濡ょ姷鍋炵敮鈥崇暦閸楃儐娓婚柟顖嗗本顥$紓鍌氬€搁崐鎼佸磹妞嬪海鐭嗗〒姘e亾閽樻繈姊洪鈧粔鎾几娴g硶鏀介柣妯挎珪閻ㄦ垹鈧鎸风欢姘跺蓟濞戙垹鐒洪柛鎰典簴婵洭姊虹粙鍖″姛闁稿繑锕㈠璇测槈濡攱鏂€闂佺硶鍓濋〃蹇斿閳ь剚淇婇悙顏勨偓鏍ь潖瑜版帒绀夐柡鍥ュ灩閻撴﹢鏌熸潏楣冩闁稿﹤顭烽弻娑㈠Ψ閵忊剝鐝栭柡宥忕節濮婄粯鎷呴崨濠傛殘闂佸湱枪椤兘骞冮悜鑺ユ櫆闁伙絽澶囬弨铏節閻㈤潧孝婵炶绠撳畷鐢稿礃椤旂晫鍘撻梺鍛婄箓鐎氼剟寮抽悢鍏肩叆婵炴垶鐟ч惌鎺撴叏婵犲洨绱伴柕鍥ㄥ姍楠炴帡骞嬪⿰鍐╃€抽梻鍌欑閹诧繝鎮烽妷锔绘闁归棿绀侀悡婵嬫煙閻愵剚鐏遍柛顐邯閺屾盯顢曢妶鍛亖闂佸憡蓱閹倿寮婚敐鍫㈢杸闁哄洨鍋橀幋椋庣磼缂併垹骞栭柣鏍帶閻g兘骞嬮敃鈧粻濠氭偣閸ヮ亜鐨洪柣銈傚亾婵犵數鍋犻幓顏嗗緤娴犲绠熼柨鐔哄Т绾捐銇勯弽顐沪闁抽攱鍨归惀顏堫敇閻愭潙娅f繛瀛樼矊缂嶅﹪骞冪捄琛℃闁哄诞鍐ㄐ曢梻浣虹《閺備線宕戦幘鎰佹富闁靛牆妫楃粭鎺楁煕閻曚礁浜伴柟顖氬暙鐓ゆい蹇撴噽閸樺憡绻涙潏鍓у埌婵犫偓鏉堛劍娅犳い蹇撶墛閻撳啴鎮峰▎蹇擃仼闁诲繑鎸抽弻鐔碱敊閻e本鍣伴悗娈垮枛閻栧ジ鐛€n喗鍋愰弶鍫厛閺佸洭姊婚崒姘偓椋庣矆娴i潻鑰块弶鍫涘妿娴犳岸姊绘担渚敯濠殿喓鍊楅崚鎺撴償閵娿儳顦梺鍦劋椤ㄥ懐鐚惧澶嬬厱妞ゆ劑鍊曢弸搴∶归悩鐑橆仩缂佽鲸鎸婚幏鍛村礈閹绘帒澹嶆俊鐐€栧ú妯荤箾婵犲洤鏋侀柛鎰靛枛绾惧吋绻涢幋鐐跺妤犵偛鐗撳缁樻媴閸涘﹥鍎撳┑鐐茬湴閸ㄨ棄鐣峰┑鍡欐殕闁告洦鍓欓埀顒€鐖奸弻锝呂熼懖鈺佺闂佺粯鎸诲ú鐔煎蓟閻斿吋鍤嬫い鎺嗗亾濠碉紕鍘ч湁婵犲﹤瀚崝銈夋煃鐟欏嫬鐏撮柡浣哥Ч瀹曠喖顢曢埄鍐╃窔闂傚倷鑳舵灙闁挎洏鍎甸幃褔鎮╅懠顒佹濠电娀娼ч鍡涘疾濠靛鐓冪憸婊堝礈閻旂厧鐏抽柨鏇炲€搁柨銈嗕繆閵堝倸浜鹃梺缁樺笒閻忔岸濡甸崟顖氱鐎广儱鐗嗛崢锛勭磽娴e搫孝濠⒀傜矙閸┾偓妞ゆ巻鍋撻柛妯荤矒瀹曟垿骞橀弬銉︽杸闂佺粯枪娴滎剛绮i弮鍫熺厱閻庯綆鍋掑▓鏃堟煃鐟欏嫬鐏存い銏$懅濞戠敻鎮滈悾灞藉冀濠电姷鏁搁崑娑㈠箯閹寸姴绶ら柛顭戝暎閿濆绠涢柡澶庢硶椤斿﹪姊洪悷鏉挎毐缁剧虎鍙冨畷浼村箻鐠囪尙顔嗛梺缁樶缚缁垶宕甸幋锔界厾缂佸娉曟禒娑欐叏閿濆棗濮嶆慨濠傤煼瀹曟帒顫濋钘変壕闁绘垼濮ら崵鍕煠閸濄儲鏆╁ù鐘崇缁绘繈鎮介棃娑楃捕濡炪倖娲﹂崣鍐ㄧ暦濡も偓铻e〒姘煎灠濞堛劌顪冮妶鍡楀闁稿﹥鐗滈埀顒佺濮樸劑鍩€椤掑倹鍤€濠㈢懓锕畷浼村冀瑜夐弸鏃堟煏婵犲繐顩紒鈾€鍋撻梻浣圭湽閸ㄨ棄岣胯閻楀海绱撴担鍝勪壕婵犮垺岣跨划鏃堟偡闁箑娈ㄩ梺鍝勮閸庤京绮婚悽鍛婄厵闁绘垶岣跨粻姗€鏌涢悙鍨毈闁哄矉缍侀幃鈺呮倻濮楀棔鍝楅梺璇茬箰缁诲牓宕濆畝鍕垫晩闊洦绋戝敮閻熸粌顦靛畷鎴﹀箻閼搁潧鏋傞梺鍛婃处閸撴瑧鍠婂鍛斀闁宠棄妫楁禍婵堢磼鐠囨彃鈧潡鏁愰悙鍓佺杸婵炴垶鐟﹂崕顏堟⒑闂堚晛鐦滈柛姗€绠栭弫宥呪堪閸愶絾鏂€闂佸疇妫勫Λ妤呮倶閻樼粯鐓欑痪鏉垮船娴滀即鏌ㄥ┑鍫濅粶妞ゆ挸銈稿畷鍫曞煛閸愯法闂繝鐢靛仩閹活亞绱炴笟鈧棢闁规崘顕х粈澶屸偓骞垮劚椤︿即鎮″▎鎴犵<閻庯綆浜炴禒銏ゆ煛閸℃稐鎲鹃柡宀嬬秮閺佹劙宕惰楠炲螖閻橀潧浠滄い鎴濐樀瀵偊宕橀鑲╁姦濡炪倖甯掗崐缁樼▔瀹ュ應鏀介柣妯虹-椤f煡鏌涚€e墎绉柡灞剧洴婵$兘骞嬪┑鍡樻婵°倗濮村ú顓㈠箖濡ゅ啯鍠嗛柛鏇ㄥ墮绾板秶绱撴担鍓叉Ч闁瑰憡濞婇崹楣冨籍閸繄顦ㄥ銈嗘煥濡插牐顦归柡灞剧洴閸╁嫰宕楅悪鈧禍顏勎涢崟顐悑闁搞儮鏅濋敍婵囩箾鏉堝墽鍒板鐟帮躬瀹曟洟骞囬悧鍫㈠幈闂侀潧枪閸庨亶銆傚畷鍥╃<妞ゆ梻鈷堥崕蹇斻亜閹惧啿鎮戠€垫澘瀚埀顒婄秵娴滄牠宕戦幘缁樼叆閻庯絻鍔嬬花濠氭⒑閻熺増鎯堢紒澶婄埣钘濋柨鏃堟暜閸嬫挸鈻撻崹顔界亪闂佽绻戠换鍫ュ春閻愬搫绠i柨鏇楀亾闁绘搫绻濋弻娑㈠焺閸愮偓鐣兼繛瀵稿閸ㄨ泛顫忓ú顏勫窛濠电姴娴烽崝鍫曟⒑閸涘﹥澶勯柛娆忛鐓ら柛娑橈梗缁诲棝鏌曢崼婵堢闁告帊鍗抽弻娑㈡偆娴e摜浠搁悗瑙勬礃閸旀瑥鐣疯ぐ鎺濇晝闁挎繂鎳庢导搴㈢節绾版ɑ顫婇柛銊﹀▕瀹曘垼顦崇紒鍌氱У閵堬綁宕橀埡浣插亾閸偅鍙忔俊顖滃帶娴滈箖鎮楀鐐

简介
为了减轻非 IT 专业人员(比如业务顾问)的负担,我们将构建带有类似于 Microsoft® PowerPoint 的演示特性的原型建模工具,以帮助业务顾问组织大量非结构化数据,并为客户准备演示文档。我们的工具的第一个版本基于 Eclipse。不过,为了利用更佳的图表功能,我们决定转向 Rich Internet Application 技术。 我们考虑了流行的 Ajax 和 Adobe Flex 的优缺点,最终决定使用 Flex。
我们的基于 Flex 的应用程序允许用户使用各种可视化辅助工具创建幻灯片,包括项列表、图表编辑器、标记云、关系搜索器和表。在本文中,我们将解释一个特殊的辅助工具,它允许用户排列、分类和比较大量数据。我们将这个辅助工具称为 “Juxtaposition Table” 或 JTable。借助 JTable,用户可以将数据可视化为二维视图,并且在运行时交互地更改水平和垂直数据集。该工具支持从一个简单视图中包含的各种用户定义透视图检查数据库或信息空间。
在查看代码之前,我们先介绍基础的场景,我们在该场景中扮演一个调查中心,负责研究公众喜欢的图书、电影、歌曲和站点的品味。一个调查组可以包含任意数量的参与者和可扩展的调查类别列表。为简单起见,我们采用很小的调查组。参与者是:John、Jennifer 和 Ivan。在完成调查之后,我们将结果传输到一个简单的数据库中。现在,我们希望这些结果能够显示在我们的 Web 站点中以供查阅。我们可以提供一个很大的 HTML 表来储存调查结果,但它在用户眼前会非常杂乱,因为表太大导致信息非常密集。我们也可以提供多个表,以从多个预定义的透视图中显示数据。不过,这种方法也不理想,因为用户被局限在我们预定义的表中,并且在多个表之间滚动可能令人烦恼。
采用 JTable 可以实现一个出色的解决方案。如果我们在 JTable 中显示数据,用户就可以更改水平和垂直的数据集,从而根据自己的信息需求选择定制的透视图。例如,用户可能希望了解 John 最喜欢的图书;这样,就可以选择 “John” 作为表的水平维度,选择 “Favorite Books” 作为表的垂直维度。用户也许希望了解调查参与者最喜欢的电影。类似地,可以选择 “All People” 作为表的水平维度,选择 “Favorite Movies” 作为表的垂直维度。通过使用 JTable,用户可以在一个方便紧凑的视图中定制自己的透视图。要运行 JTable 应用程序,可以在下面的 下载表 中下载 DataGridDeveloperWorksExample1。运行 JTable 应用程序之后,您就可以以各种组合方式查看调查的结果。
我们使用 Flex 的 AdvancedDataGrid 组件来实现 JTable。在这个分为两部分的系列文章中,我们将演示如何定制和扩展在 Flex 3 数据可视化包中提供的 AdvancedDataGrid 组件。在本文中,我们还演示如何使用水平和垂直控制条在运行时修改 JTable 的内容,以及如何将表的第一列和第一行转变成列标题栏和行标题栏。在第 2 部分中,我们将演示在单元格中流畅地扩展和收缩项,以及以拖放的方式在单元格之间移动项。
本系列文章假设读者熟悉 Flex 编程环境和 ActionScript 语言。
通过运行示例比较模拟表和 JTable
在查看 JTable 应用程序的实现解决方案之前,我们先了解从调查中收集到的数据的模拟透视图,然后再概述我们的 JTable。
我们需要实现这样一个表,它包含一个用于显示一个人的姓名或所有人的姓名的水平标题栏,以及一个用于显示一个类别或喜欢的项的所有类别的垂直标题栏。表 1 模拟用户对水平维度选择 “All People” 并对垂直维度选择 “All questions” 之后的表。
表 1. 显示调查组最喜欢的项的 2D 模拟表
John | Jennifer | Ivan | |
Favorite book | Book - 1 | Book - 2 | Book - 3 |
Favorite movie | Movie - 1 | Movie - 2 | Movie - 3 |
Favorite song | Song - 1 | Song - 2 | Song - 3 |
Favorite site | Site - 1 | Site - 2 | Site - 3 |
图 1 显示了使用 JTable 实现的概念。
图 1. 显示调查组最喜欢的项的 2D JTable
图片看不清楚?请点击这里查看原图(大图)。
图 1 是一个二维的数据查找图。从该图可以看到,通常出现在表中的标题栏标签没有出现在这里(在表的顶部稍微可以看到标题栏,因为包含垂直表线的窄栏允许用户改变列的大小)。您还可以观察到表体之外有两个外观一样的标题栏,它们分别是垂直和水平的。在本文的后面,我们将使用两个术语:标题栏单元格 和 内容单元格。我们将标题栏单元格定义为顶部标题栏或侧边标题栏的单元格;并将内容单元格定义为在表体中而不是标题栏中的单元格。如果再仔细查看图 1,您将看到每个标题栏单元格仅包含一个包围在椭圆形中的项,而内容单元格包含好几个项,并且每个项都有更深色的背景。另外,图中还显示了两个菜单按钮,第一个在水平标题栏的上边,第二个在垂直标题栏的左边。这两个按钮的下拉菜单允许用户动态地配置标题栏的内容,并且表的内容将随之而变。
在查看 JTable 应用程序的实现解决方案之前,我们先展示如何生成运行 JTable 应用程序所需的数据。
生成数据
在真实的场景中,调查数据很可能来自服务器的数据库。不过,为了配合这个例子,我们使用简单的 DataBase 类,并使用调查组成员给出的答案填充它。问题本身被存储为类别。我们将数据库当作一个单实例对象,以将 DataBase 类限制为 1 个实例。下面给出类访问该实例的代码行:
var fRowSet = DataBase.instance.getQuestions();
我们将所有 参与者的答案储存在一个储存库中,并将参与者的答案分别与对应的姓名关联起来。答题者的姓名作为查询储存库的键。我们将每个问题的答案储存在一个独立的库中,每个储存库的键与问题相同,比如 “Favorite Book”、“Favorite Movie”、“Favorite Song” 和 “Favorite Site”。使用答案填充每个参与者的储存库之后,我们将把他们的答案添加到所有答案储存库(fMap)中。
清单 1. 设置 John 的答案
var fMap:Hashmap = new Hashmap();
var johnsAnswers:Hashmap = new Hashmap();
var johnsBooks:Array = new Array("The Human Stain", "One More Year","The Painted Bird");
johnsAnswers.put("Favorite Books", johnsBooks);
…
fMap.put("John", johnsAnswers);
我们以类似的方式将 Jennifer 和 Ivan 的答案储存到所有答案储存库中。然后,我们使用 DataBase 类中的 3 个方法获取数据:
清单 2. 获取数据
public function getPeople():Array
public function getQuestions():Array
public function getAnswers(person:String = null, question:String = null):Array
getPeople 方法将所有调查参与者所在的数组作为字符串返回。getQuestions 方法将所有调查类别所在的数组作为字符串返回。最后,getAnswers 方法接受两个参数作为输入(人名和调查类别)并将答案数组作为字符串返回。我们使用这些方法返回的数组结构构建我们的数据模型。可以使用 null 人名或问题参数调用 getAnswers 操作。
本文的剩余部分将讨论我们的 JTable 设计和实现解决方案,包括我们对 AdvancedDataGrid 组件进行的必要修改和我们创建的其他支持,从而让 JTable 能够像例子所展示的那样运行。
建模数据
JTable 数据提供程序构建在行和单元格的基础之上。因为表是由行组成的,并且每个行包含多个单元格,所以我们首先定义 Row 类和 Cell 类,以实现数据提供程序和表之间的映射。
如这些类的名称所示,Row 类存储每个行的对象,而 Cell 类用于储存每个行的单元格的内容,见下面的清单 3。每个单元格都包含一个泛型数据变量。我们将在该数据变量中储存包含两个元素的数组:单元格的水平标题栏名和垂直标题栏名。
清单 3. Cell 类
public class Cell
{
private var fData:*;
public function Cell()
{
}
public function get data():*
{
return fData;
}
public function set data(value:*):void
{
fData = value;
}
}
我们创建一个称为 HeaderCell 的包装器类,用于区分标题栏单元格和内容单元格。此外,我们还创建了另一个包装器类 NullCell,用于表示左上角中既不是 “标题栏” 单元格又不是 “内容” 单元格的单元格。
行内容由其 Cell 对象定义。Row 类是 Cell 对象的包含者,如清单 4 所示。Row 类是动态的。Flex 中的动态对象充当散列映射(hashmap)。当 cell 对象属于向其添加的行时,将在运行时修改 Row 类。Cell 对象通过类索引插入到 Row 对象中。因此,Cell 对象通过 Row 对象中的列索引进行散列化。
清单 4. Row 类
public dynamic class Row
{
private var fColumIndex:int = 0;
public function Row()
{
}
public function putCell(cell:Cell):void
{
this[fColumIndex++] = cell;
}
public function getCellAt(index:int):Cell
{
return this[index] as Cell;
}
}
查看了 Cell 和 Row 类之后,我们将进入 JTable 应用程序的另一个实现解决方案。首先,我们展示如何保护 JTable 的 UI 代码不因任何数据变化而遭到更改。我们通过将数据与用户界面分离来实现该目的。
我们在 DefaultDataModel 类中定义表的数据模型。该类的 createDataPovider 函数为表提供内容。这个函数接受两个参数:“行数组” 和 “列数组”。这些参数被传递到 DefaultDataModel class 构造器,如清单 5 中的代码所示。
清单 5. 将参数传递到 DefaultDataModel 类
private var fRowSet:Array;
private var fColumnSet:Array;
public function DefaultDataModel(rowSet:Array, columnSet:Array)
{
fRowSet = rowSet;
fColumnSet = columnSet;
}
每次用户想要通过选择菜单选项(水平或垂直弹出菜单按钮)改变表的外观时,将按照清单 6 的代码修改表背后的数据模型。
清单 6. 更改数据模型
public function get dataProvider():ArrayCollection
{
if(!fDataProvider)
fDataProvider = createDataProvider();
return fDataProvider;
}
从清单 7 的代码可以看到,createDataProvider 函数将代码分成 3 个其他函数:createRowAndColumnDataProvider、createColumnOnlyDataProvider 和 createRowOnlyDataProvider。createRowAndColumnDataProvider 创建一个为 2D 表提供内容的数据提供程序。createColumnOnlyDataProvider 和 createRowAndColumnDataProvider 创建为 1D 表提供内容的数据提供程序。
当用户选择 “Clear” 选项时,将在水平菜单按钮或垂直菜单按钮中显示一个 1D 表(见图 2 和图 3)。
清单 7. createDataProvider
protected function createDataProvider():ArrayCollection
{
if (fRowSet && fColumnSet)
return createRowAndColumnDataProvider();
if (fColumnSet && !fRowSet)
return createColumnOnlyDataProvider();
if (fRowSet && !fColumnSet)
return createRowOnlyDataProvider();
return new ArrayCollection();
}
在下一个代码片段中(清单 8),我们将展示 3 个 “Create” 数据提供程序函数之一,即 createRowAndColumnDataProvider。这个函数为 2D 表提供内容。首先,我们实例化一个标题行并向其添加一个左上角单元格。然后,我们将遍历列(第一次循环)并将其余的标题栏单元格添加到该行,并在遍历结束之后将该行添加到提供程序。第二次循环在行上进行。对于 fRowSet 数组中的每个元素都实例化一个新行。向行添加的第一个单元格是一个水平标题栏单元格;向行添加的其他单元格是内容单元格。
清单 8. DefaultDataModel 类:createRowAndColumnDataProvider 函数
protected function createRowAndColumnDataProvider():ArrayCollection
{
var provider:ArrayCollection = new ArrayCollection();
var headerRow:Row = new Row();
// Put in the top corner cell
headerRow.putCell(new NullCell());
// Populate the row header
for each(var item:String in fColumnSet)
{
// Create the header cells
var cell:Cell = new HeaderCell;
cell.data = item;
headerRow.putCell(cell);
}
provider.addItem(headerRow);
// Populate the contents and column header
for each(var rowItem:String in fRowSet)
{
// Content row
var row:Row = new Row();
// Column header cell
var headerCell:Cell = new HeaderCell();
headerCell.data = rowItem;
row.putCell(headerCell);
for each(var columnItem:String in fColumnSet)
{
// Create the content cells
var cell:Cell = new Cell();
cell.data = new Array(rowItem,columnItem);
row.putCell(cell);
}
provider.addItem(row);
}
return provider;
}
从 createRowAndColumnDataProvider 函数返回的数组集合将通过表的 dataProvider 属性分配给表。
我们构建另一个数据模型为控制标题栏和表的内容的水平和垂直菜单按钮提供内容。我们在 PopUpButtonsDataModel 类中定义该模型。这个类仅有两个函数:getRowMenu 和 getColumnMenu。您可以在下面的代码片段(清单 9)中看到这两个函数之一。我们首先向菜单数组集合添加 “Clear” 和 “All Questions” 菜单项,然后在一个简单的循环中添加调查类别。我们储存在每个菜单项对象中的数据是一个仅包含一个元素的数组:类别名。从 getRowMenu 和 getColumnMenu 返回的 MenuItem 对象的数组集合将通过它们的 dataProvider 属性分配给菜单按钮。
清单 9. PopUpButtonsDataModel class: getRowMenu 函数
public function getRowMenu(): ArrayCollection
{
var menu:ArrayCollection = new ArrayCollection();
var questions:Array = DataBase.instance.getQuestions();
var menuItem:MenuItem = new MenuItem("Clear");
menu.addItem(menuItem);
var menuItem:MenuItem = new MenuItem("All questions");
menuItem.data = questions;
menu.addItem(menuItem);
for each(var question:String in questions)
{
var menuItem:MenuItem = new MenuItem(question);
menuItem.data = new Array(question);
menu.addItem(menuItem);
}
return menu;
}
构建 UI
在图 1、2 和 3 中可见的 UI 是一个画布容器,它们包含 3 个用户界面元素:表和两个菜单按钮。我们的画布类 DynamicAdvancedDataGridCanvas 扩展了 Flex 的 Canvas 类。我们在 createChildren 函数中将表和两个菜单按钮添加到画布容器中(清单 11),该函数将覆盖基类中的相同函数。我们使用菜单按钮允许用户通过从按钮下拉菜单中选择选项来查看调查。(这里的按钮为水平和垂直控制条)。我们通过将行逆时针旋转 90 度角创建垂直控制条,如下面的清单 11 所示。不过,这意味着需要嵌入字体,因为标准的字体不能显示在垂直按钮中。清单 10 显示了在应用程序的 DataGridDeveloperExample1.mxml 文件中包含嵌入字体声明所需的代码。
清单 10. 在 DataGridDeveloperExample1.mxml 中嵌入字体所需的代码
[Embed(systemFont='Verdana', fontWeight="bold", fontName='embeddedFont',
mimeType='application/x-font', advancedAntiAliasing="true")]
清单 11. DynamicAdvancedDataGridCanvas 类:createChildren 函数
override protected function createChildren():void
{
super.createChildren();
fColPopupMenuButton = new SuperPopUpMenuButton();
...
PopupMenuButton.addEventListener(MenuEvent.ITEM_CLICK, columnClick);
fColPopupMenuButton.addEventListener(DropdownEvent.OPEN, colPopupOpen);
fColPopupMenuButton.dataProvider =
fPopUpButtonsDataModel.getColumnMenu().toArray();
addChild(fColPopupMenuButton);
fRowPopupMenuButton = new SuperPopUpMenuButton();
...
fRowPopupMenuButton.rotation = -90;
...
fRowPopupMenuButton.addEventListener(MenuEvent.ITEM_CLICK, rowClick);
fRowPopupMenuButton.addEventListener(DropdownEvent.OPEN, rowPopupOpen);
...
fRowPopupMenuButton.dataProvider = fPopUpButtonsDataModel.getRowMenu().toArray();
addChild(fRowPopupMenuButton);
fAdvancedDataGrid = new DynamicAdvancedDataGrid();
addChild(fAdvancedDataGrid);
...
}
我们向画布添加的菜单按钮是受事件驱动的,这意味着当用户从下拉菜单中打开和选择选项时,canvas 类将处理生成的事件(DropdownEvent.OPEN 和 MenuEvent.ITEM_CLICK 事件)。
图 2. 显示调查组最喜欢的项的 1D 表,打开了垂直菜单
图片看不清楚?请点击这里查看原图(大图)。
图 3. 显示调查组最喜欢的项的 1D 表,打开了水平菜单
图片看不清楚?请点击这里查看原图(大图)。
在下一个代码片段中(清单 12),我们将显示如何处理 DropdownEvent。通过菜单按钮的 dataProvider 属性来分配数据提供程序。您有必要了解如何将垂直菜单按钮的 popUp 窗口的 y 坐标从局部坐标转换成全局坐标(见代码片段的最后一行)。水平菜单按钮不需要进行此类转换。
清单 12. DropdownEvent 处理
public function rowPopupOpen(event:DropdownEvent):void
{
if(fRowMenuInvalid)
{
fRowPopupMenuButton.dataProvider =
fPopUpButtonsDataModel.getRowMenu().toArray();
}
fRowPopupMenuButton.popUp.y =
localToGlobal(new Point(0,fRowPopupMenuButton.y)).y
- fRowPopupMenuButton.width;
}
在下面的清单 13 中,我们将显示如何处理第二个事件 MenuEvent。第一行代码更改按钮的标签,以在下拉菜单中显示选择的标签。第二行代码从事件获取菜单数据对象。该函数的最后一行代码刷新表。
清单 13. MenuEvent 处理
private function rowClick(event:MenuEvent):void
{
fRowPopupMenuButton.label = event.label;
fRowSet = (event.item as MenuItem).data;
refresh();
}
清单 14 显示了 refresh 函数。
清单 14. Refresh 函数
public function refresh():void
{
var model:DefaultDataModel = new DefaultDataModel(fRowSet,fColumnSet);
fAdvancedDataGrid.dataProvider = model.dataProvider;
}
现在,让我们更加详细地探索表本身的创建工作。我们在 DynamicAdvancedDataGrid 类中定义表,该类扩展 Flex 的 AdvancedDataGrid 类。
Flex AdvancedDataGrid 是一个包含在 Flex 数据可视化包中的 UI 包,它能够显示多列信息。AdvancedDataGrid 组件的每个列都由一个 AdvancedDataGridColumn 对象表示。headerText 和 dataField 属性是定义一个列的两个主要属性。headerText 是显示为列标题的名称,而 dataField 表示来自数据提供程序的数据将显示在该字段中。由对象集合组成的数据通过 dataProvider 属性分配给 AdvancedDataGrid 组件。
清单 15 显示了创建包含 AdvancedDataGrid 组件的应用程序的最简单例子。在该代码片段中,我们将变量 dataModel 绑定到 dataProvider 属性。
清单 15. 使用 AdvancedDataGrid 创建一个简单的应用程序
private var dataModel:ArrayCollection =
new ArrayCollection([
{Person :"John", Favorite Book: "The Human Stain"},
{Person :"Jennifer", Favorite Book: "One More Year"},
{Person :"Ivan", Favorite Book: "The Painted Bird"}]);
fAdvancedDataGrid = new AdvancedDataGrid();
fAdvancedDataGrid.dataProvider = dataModel;
fAdvancedDataGrid.percentWidth = 100;
fAdvancedDataGrid.percentHeight = 100;
addChild(fAdvancedDataGrid);
要运行这个简单的例子,请从下面的 下载表 下载 DeveloperWorksDataGridBaseExample。在这个例子中,根据数据提供程序的格式和内容在表中显示了两个列(Person 和 Favorite Book)。
列会自动从数据提供程序获取 “键” Person 和 Favorite Book,以确定在显示信息时应该使用该数据提供程序的哪个字段。尽管使用这些代码和默认的 AdvancedDataGrid 就可以创建一个应用程序,但是该组件在实现我们的 JTable 时会受到限制。我们发现的 3 大限制是:数据提供程序的原始格式、列数固定和缺少单元格着色支持。原始数据提供程序不能满足经常改变的数据的需求,因为组件不能收到数据变更通知。我们对 JTable 使用的解决方案是在每次用户从按钮的下拉菜单选择新的选项时,通过重新分配数据提供程序来刷新表。我们在数据发现变化时动态地重新构建列,从而解决了固定列数的限制。如您即将在清单 16 中见到的一样,我们在显示数据之前使用呈现器定制它,并为单元格中的各个项着色。
在这个小节开始时,我们展示如何从 DynamicAdvancedDataGridCanvas 的 createChildren 函数向 DynamicAdvancedDataGrid 发出一个创建表的调用。我们还展示了在每次刷新表之后,如何给表分配重新创建的数据提供程序。向表分配数据提供程序的操作发生在 DynamicAdvancedDataGrid 类的 “set” 函数中。这个 “set” 函数覆盖基类中的 “set” 函数。该函数的最后一行是对 createColumns 函数的调用,后者在表中动态地创建列:
清单 16. 定制数据的呈现器
override public function set dataProvider(value:Object):void
{
super.dataProvider = value;
createColumns(dataProvider as ArrayCollection);
}
以数组集合的形式存在的表内容被作为参数传递给 createColumns 函数。数组集合包含的所有行都将显示在表中。每个行包含的列对象与将显示在表中的列一样多。对于 createColumn 函数,我们首先需要做的就是找出一个行有多少个列。这通过计算第一行包含的列对象数获得,我们将得到的计数作为参数传递到 DynamicAdvancedDataGrid 类的 getNumberOfProperties 函数。在确定了列的数量之后,我们将遍历该计数,并使用 Flex 的 AdvancedDataGridColumn 类创建列。从 createColumns 函数的代码片段(清单 17)可以看到,列的 dataField 被分配一个序号,它表示列对象出现在数据提供程序集合的行的次序。我们还为每个列分配其他属性(比如 “resizable” 和 “visible”),并将列放入列数组 “theColumns” 中。当遍历结束之后,我们将列数组分配给表的 “columns” 属性。这由 createColumn 函数的最后一个语句完成。
清单 17. DynamicAdvancedDataGrid 类:createColumns 函数
private function createColumns(dp:ArrayCollection): void
{
if(dp.length > 0)
{
var numColumns:int = getNumberOfProperties(dp[0]);
var theColumns:Array = new Array();
for(var i:int = 0; i < numColumns; i++)
{
var dgc:AdvancedDataGridColumn =
new AdvancedDataGridColumn();
dgc.dataField = i.toString();
...
theColumns.push(dgc);
}
columns = theColumns;
}
}
在进入 UI 实现的最后部分(定制项呈现器)之前,我们需要指出的是,在 DynamicAdvancedDataGrid 类的构造器中,我们将定制项呈现器(包装在 ClassFactory 对象中的 DynamicAdvancedDataGridItemRenderer)分配给表的 itemRenderer 属性:
itemRenderer = new ClassFactory( DynamicAdvancedDataGridItemRenderer);
项呈现器允许显示定制数据。在向用户显示数据之前,我们通过创建自己的项呈现器来定制数据。我们的呈现器基于 Flex 的 HBox 组件,并覆盖该组件的 set 函数。我们在当前的行数据对象传递到项呈现器之后修改它。
如前所述,内容单元格的主体中可以包含多个项。John 可以有 3 本最喜欢的图书,Ivan 可以有两部最喜欢的电影。在与 “Favorite books” 和 “John” 相关联的单元格中必须显示 3 个项,而与 “Favorite Movies” 和 “Ivan” 相关联的单元格中必须显示 2 个项。
因此,我们决定通过我们称之为 “项查看器(item viewer)” 的工具在单元格中显示每个元素。“项查看器” 是一个包含文本字段的 “框”。项呈现器通过它的 set 函数在单元格中创建数量与单元格中的答案一样多的项查看器。ItemViewer 类扩展 Box 类。它包含一个 UI 组件 SuperUITextField,该组件继承 UITextField。
下面是 DynamicAdvancedDataGridItemRenderer 的 set 函数的代码片段。注意,在该代码片段(清单 18)中,将查询数据库中属于某个内容单元格的所有答案。在获取答案之后,将通过遍历这些答案创建项查看器。
清单 18. DynamicAdvancedDataGridItemRenderer 类:来自 set 函数的代码片段
if(data && data is Row)
{
var cell:Cell = (data as Row).getCellAt(listData.columnIndex);
if (cell is NullCell) return;
if(cell is HeaderCell)
{
var viewer:ItemViewer = new ItemViewer();
viewer.styleName = header;
...
viewer.title = cell.data as String;
addChild(viewer);
}
else
{
// content cell
var parameters:Array = cell.data as Array;
var answers:Array =
DataBase.instance.getAnswers(parameters[1],parameters[0])
for each(var answer:String in answers)
{
var viewer:ItemViewer = new ItemViewer();
viewer.styleName = "content";
viewer.title = answer;
addChild(viewer);
}
}
}
我们想提及的最后实现细节是查看器的 styleName 属性,它用于调整查看器的颜色和形状。在以上代码片段中,为每个查看器分配了一个 styleName 属性。如果恰好在标题栏单元格中创建查看器,那么查看器的 styleName 为 “header”。对于其他情况,styleName 为 “content”。“header” 和 “content” 查看器的背景颜色、圆角半径、边框和透明度都在外部 CSS 文件 example.css 的类选择器中定义(见清单 19)。
清单 19. 定制 header 和 content 查看器的样式
.header
{
borderStyle : solid;
backgroundColor: #FFFFFF;
cornerRadius: 8;
borderThickness: 1;
}
.content
{
backgroundColor: #AABBCC;
cornerRadius: 8;
}
结束语
现在,您了解了我们如何创建基于 Flex 的并置表,希望您也尝试通过改变水平和垂直数据集来创建自己的定制透视图。如果您的应用程序需要动态地创建列、显示定制数据或使用独特的单元格外观和颜色,那么可以应用本文介绍的理念和实现。
在下一篇文章中,我们将展示如何流畅地扩展和收缩单元格中的项,以及如何以拖放的方式在单元格之间移动项。
- ››创建SQL2005自动备份,定期删除的维护计划
- ››创建动态表单 javascript
- ››创建基于PPTP的站点到站点VPN连接:ISA2006系列之...
- ››创建基于L2TP的站点到站点的VPN连接:ISA2006系列...
- ››创建一个Twisted Reactor TCP服务器
- ››创建Windows Mobile上兼容性好的UI 程序
- ››创建android的Service
- ››创建远古部落环境与原住民角色
- ››创建并扩展Apache Wicket Web应用
- ››创建不在任务条中显示窗口按钮的应用
- ››创建 Android 文件系统(Root file system)
- ››创建 Android 系统步骤简介
更多精彩
赞助商链接