はじめに
画面構成が複雑でコントロールが多くなると、どうしても画面表示に時間がかかります。今回は画面表示を早くするために工夫したことについて書いていきます。
前回の記事
レスポンスが問題となったデータ入力画面
データ一覧で選択したデータの詳細を表示して編集する画面です。
テーブルのカラム数が少ない場合は問題ないのですが、多くなると表示まで2~3秒ほどの待ち時間が発生してしまいます。
表示方法
Tableを使用した表示
Curlにはhtmlのテーブルと同じレイアウトが可能な「Table」があります。これは、表形式の表示には便利なため、当初はこの方式を利用してデータを表示していました。
{value let t:Table = {Table border-width = 1pt, vertical-line-width = 1pt, horizontal-line-width = 1pt, cell-border-color="#cccccc", background="#ffffff"} {t.add {cell background="#dcdcdc",ヘッダー1}, row=0, column=0}||背景色を変えることで、ヘッダーにみせることができます。 {t.add {cell background="#dcdcdc",ヘッダー2}, row=0, column=1} {t.add {cell background="#dcdcdc",ヘッダー3}, row=0, column=2} {t.add "(1,0)のセル", row=1, column=0} {t.add "(1,1)のセル", row=1, column=1} {t.add "(1,2)のセル", row=1, column=2} {t.add "(2,0)のセル右と結合",colspan=2, row=2, column=0} {t.add "(2,2)のセル下と結合", rowspan=2,row=2, column=2} {t.add "(3,0)のセル", row=3, column=0} {t.add "(3,1)のセル", row=3, column=1} t }
しかし、テーブルのカラム数が少ない場合は良いのですが、カラムが100を超えるものになってくると、レスポンスが悪くなります。ボトルネックがどこにあるのかを調査したところ、画面に表示する処理がその原因であることが分かりました。
Curlでは(ほとんどの言語共通かもしれませんが)、コントロールを作成するのは瞬時でも、表示にはどうしても時間がかかってしまいます。このままでは「軽快に動く」を実現できないので、違う方法を試すことにしました。
解決方法
実際に試したのは、「TabContainer」を使った分割表示と「RecordGrid」を使用した表示です。
1.TabContainerを使用して表示
Curlにはタブレイアウトができる「TabContainer」があります。これを使って入力項目を分けて表示すれば、一度に表示する項目が減るので少し速くなるはずです。
{TabContainer {TabPane height=200pt,width=200pt, {text font-size = 28pt, 1} }, {TabPane {text font-size = 28pt, 2} }, {TabPane {text font-size = 28pt, 3} } }
TabContainerを使い、入力画面を次のように項目ごとにタブ表示しました。
100あるカラムを20くらいずつに分けて表示したところ、確かにレスポンスは良くなりました。しかし、テーブルカラムの分類が必ず20カラム以下になるとは限りません。そこで、次に「RecordGrid」を試すことにしました。
2.RecordGridを使用して表示
RecordGridは、テキストだけでなくチェックボックスやテキストボックスなどのコントロールを表示できます。
RecordGridは、画面に表示されているものだけが描画されます。例えば1000件をグリッドにセットしても、実際に描画されるのは数十件程度のデータである場合が多いです。RecordGridを使って表示するデータ量を減らすことで、結果的に表示速度を速めることができるというわけです。
ちなみに、スクロールするたびに再描画が必要となるという面もありますが、それで極端にレスポンスが落ちることもありません。
RecordGridとTabContainerを併用して表示したところ、1秒以下で入力画面が表示されるようになりました(※Tableの時と外見は変わっていませんが、RecordGridを使用しています)。
TableとRecordGridの速度比較
下図のように1000行を表示するプログラムを「Table」、「RecordGrid」を使ってそれぞれ作成し、速度を比較してみました。
||StopWatchクラスで処理時間計測開始 {let v_sw:StopWatch = {StopWatch}} {v_sw.start} {let v_tbl:Table = {Table border-width = 1pt, vertical-line-width = 1pt, horizontal-line-width = 1pt, cell-border-color="#cccccc", background="#ffffff"}} {for x:int=1 to 1001 do ||1行面は見出し {if x == 1 then {v_tbl.add {row-prototype height=20pt, {cell background="#B0D0FF", {TextFlowBox width = 150pt, "カラム名"} }, {cell background="#B0D0FF", {TextFlowBox width = 150pt, "データ"} } } } else {v_tbl.add {row-prototype height=20pt, {cell {TextFlowBox width = 150pt, "カラム名" & (x - 1)} }, {cell {TextField width = 150pt} } } } } } {let v_box:ScrollBox = {ScrollBox width = 330,height = 300,v_tbl}} {value v_box} ||処理時間出力 {output "Tableを使って作成:" & {v_sw.elapsed} & "で表示完了"}
||RecordGridのセルにコントロールをセットするためのclass定義---------- {define-class public open anyCell {inherits StandardRecordGridCell} {constructor public {default} {construct-super} set self.margin =2pt } {method public open {refresh-data}:void {self.clear} let (data:any, valid?:bool) = {self.get-data} {self.add-internal data} } } ||RecordGridのセルにコントロールをセットするためのclass定義----------END ||StopWatchクラスで処理時間計測開始 {let v_sw:StopWatch = {StopWatch}} {v_sw.start} {let v_box:VBox = {VBox}} {let v_recFieldsArray:{Array-of RecordField} = {new {Array-of RecordField}}} ||RecordGridのフィールド(列)定義 {v_recFieldsArray.append {RecordField "colName",caption="列名", domain = any} } {v_recFieldsArray.append {RecordField "dataField",caption="データ", domain = any} } ||RecordGridのフィールド(列)の型定義(ここのcell-specでカスタムセルを指定してコントロールを表示する) {let v_recGridColumnArray:{Array-of RecordGridColumn} = {new {Array-of RecordGridColumn}}} {v_recGridColumnArray.append {RecordGridColumn "colName", sort-spec=null,cell-spec=anyCell,width=150pt} } {v_recGridColumnArray.append {RecordGridColumn "dataField", sort-spec=null,cell-spec=anyCell,width=150pt} } ||RecordFieldをもとにRecordSetを作成 {let v_recSet:RecordSet = {RecordSet {RecordFields {splice v_recFieldsArray}}}} ||RecordSetに1000件のデータを追加 {for x:int=1 to 1000 do let v_record:RecordData = {RecordData} {v_record.set "colName", {TextFlowBox width = 150pt, "カラム名" & x} } {v_record.set "dataField", {TextField width = 150pt} } {v_recSet.append v_record} } ||RecordField,RecordGridColumn,RecordSetをもとにRecordGridを作成 {let v_recGrid:RecordGrid = {RecordGrid grid-line-color = "#758294", alternate-row-background = "#E1E1E1", select-current-record? = false, cells-take-focus? = false , width = 320, height = 300, header-options ={RecordGridRowOptions background = "#B0D0FF",halign = "left"}, background="white", record-source = v_recSet, display-navigation-panel? = false, display-record-selectors? = false, {splice v_recGridColumnArray}} } {value v_box} {v_box.add v_recGrid} ||処理時間出力 {output "RecordGridを使って作成:" & {v_sw.elapsed} & "で表示完了"}
この結果は、Tableを使った場合が「1.063秒」で、RecordGridの場合が「0.091s」でした。 次に、データ件数を100件に減らして試してみたところ、Tableが「0.093秒」、RecordGridの場合が「0.019s」でした。
以上の結果から、RecordGridを使うと、データ件数の増減から受ける影響はさほどなく、ほぼ同じレスポンスで処理できることがわかります。
まとめ
画面描画にかかる時間を短縮したい場合、配置するコントロールの数を少なくするのが1番ですが、そうもいかない場合は「TabContainer」や「RecordGrid」の利用を検討してみてはいかがでしょうか。また、よく確認すると「HBox」や「VBox」など必要のないコンテナが画面に含まれている場合もあります。こういったものもレスポンスを悪くする原因になるので、画面のコントロールを今一度見直してみると、改善につながるかもしれません。