OperaMasks提供了一個(gè)功能靈活而又簡單易用的Tree作用組件,包括以下特性:
支持靜態(tài)定義樹節(jié)點(diǎn),也支持從服務(wù)器端異步裝載節(jié)點(diǎn)數(shù)據(jù)
提供Ajax方式的事件監(jiān)聽器,可監(jiān)聽onclick,oncheck,onselect、onexpandnode和oncollapsenode事件
可定制樹節(jié)點(diǎn)的顯示,例如text和icon,也可以控制節(jié)點(diǎn)前面是否有選擇框
提供了一組服務(wù)器端API,可編程控制樹的行為
這篇文章將通過三個(gè)簡單示例,向您介紹如何使用OperaMasks Tree組件。
OperaMasks Tree的最簡單的使用方式,是使用靜態(tài)Tree節(jié)點(diǎn)。在一些場合下,這是滿足需求的。讓我們先看一下第一個(gè)示例的效果圖:
該示例模擬一個(gè)Windows的Explorer,當(dāng)用戶使用鼠標(biāo)操作樹(單擊、雙擊、展開和收縮樹節(jié)點(diǎn))時(shí)。顯示接收到的事件的類型和影響到的節(jié)點(diǎn)。
所有示例的代碼都分成兩部分,一部分是頁面代碼,另一部分是服務(wù)器端ManagedBean代碼。首先來看第一個(gè)示例的頁面源碼:
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"xmlns:ajax="http://www.apusic.com/jsf/ajax" renderKitId="AJAX"xmlns:h="http://java.sun.com/jsf/html"><w:page title="靜態(tài)樹"><w:head><w:stylesheet src="/common/resources/examples.css" /></w:head><layout:borderLayout fitToBody="true"><layout:panel region="north" header="false" height="50" border="false"><div class="examDesc"><p>本例演示靜態(tài)樹</p></div></layout:panel><layout:panel region="west" width="150" title="tree" split="true"><w:tree id="tree" loadAllNodes="true" border="false" rootVisible="true"expandAll="true" style="width:100%;height:100%;"><w:treeNode text="root" icon="" checked="true"><w:treeNode text="node1" icon="" checked="true"><w:treeNode text="node11" leaf="true" checked="false" icon=""></w:treeNode><w:treeNode text="node12" leaf="true" checked="false" icon=""></w:treeNode><w:treeNode text="node13" leaf="true" icon=""></w:treeNode></w:treeNode><w:treeNode text="node2" checked="true" cascade="true" icon=""><w:treeNode text="node21" checked="true" cascade="true" leaf="true"icon=""></w:treeNode><w:treeNode text="node22" checked="true" cascade="true" leaf="true"icon=""></w:treeNode><w:treeNode text="node33" leaf="true" icon=""></w:treeNode></w:treeNode><w:treeNode text="node3" icon=""><w:treeNode text="node31" leaf="true" icon=""></w:treeNode></w:treeNode></w:treeNode></w:tree></layout:panel><layout:panel region="center" title="message"><h:outputText id="response" escape="false"></h:outputText></layout:panel></layout:borderLayout></w:page></f:view>
頁面對應(yīng)的StaticNodesTree的ManagedBean,代碼如下:
package demo.tree;import org.operamasks.faces.annotation.Action;import org.operamasks.faces.annotation.Bind;import org.operamasks.faces.annotation.ManagedBean;import org.operamasks.faces.annotation.ManagedBeanScope;import org.operamasks.faces.component.tree.impl.UITree;import org.operamasks.faces.component.tree.impl.UITreeNode;@ManagedBean(name="StaticTreeBean", scope=ManagedBeanScope.REQUEST)public class StaticTreeBean {@Bindprivate UITree tree;@Bindprivate String response;@Actionpublic void tree_onselect(){process();}@Actionpublic void tree_oncheck(){process();}private void process(){response = "響應(yīng)事件的節(jié)點(diǎn)是:" + tree.getEventNode().getText();response += "<br/>";response += "勾中的節(jié)點(diǎn)是:";for(UITreeNode node : tree.getCheckedNodes()){response += node.getText() + ",";}response += "<br/>";response += "根節(jié)點(diǎn)的直接后代中半勾中的節(jié)點(diǎn)是:";for (UITreeNode node : tree.getRootNode().getPartlyCheckedChildren()) {response += node.getText() + ",";;}response += "<br/>";response += "全樹范圍內(nèi)半勾中的節(jié)點(diǎn)是:";for (UITreeNode node : tree.getPartlyCheckedNodes()) {response += node.getText() + ",";}response += "<br/>";if(tree.getSelectedNode() != null){response += "選中的節(jié)點(diǎn)是:" + tree.getSelectedNode().getText();}}}
在Tree組件的實(shí)際應(yīng)用中,更常見的是通過Server端程序,動態(tài)的裝載樹節(jié)點(diǎn)。OperaMasks Tree支持采用異步的、按需獲取的方式裝載數(shù)據(jù)。即只有在一個(gè)樹節(jié)點(diǎn)需要被展開時(shí),組件才通過Ajax的方式從服務(wù)端獲取該節(jié)點(diǎn)的子節(jié)點(diǎn)數(shù)據(jù)。對于樹節(jié)點(diǎn)的數(shù)量很大的情形,這種模式是靈活且高效的。
下面,我們將通過一個(gè)示例來展示Tree組件的動態(tài)能力,這個(gè)示例的效果圖如下:
這個(gè)例子看上去似乎和示例一一樣,其實(shí)不然,這個(gè)例子工作在動態(tài)數(shù)據(jù)裝載模式下。
要能正確理解OperaMasks Tree的用法,需要解釋一下業(yè)務(wù)對象(Business Object),用戶數(shù)據(jù)對象(User Data Object)及它們之間的關(guān)系。一般來說,樹的每一個(gè)節(jié)點(diǎn)都表示一個(gè)業(yè)務(wù)對象,比如Company,Department和User,我們用樹的結(jié)構(gòu)來表現(xiàn)這些業(yè)務(wù)對象之間的關(guān)聯(lián)。把業(yè)務(wù)對象直接綁定到樹節(jié)點(diǎn)上,無疑是直觀的,但并非一個(gè)好的開發(fā)實(shí)踐。原因是當(dāng)業(yè)務(wù)對象是較重量級的大對象時(shí),直接把業(yè)務(wù)對象綁定在樹節(jié)點(diǎn)上,會導(dǎo)致性能方面的問題,這在Web環(huán)境下尤為嚴(yán)重。
為了解決業(yè)務(wù)對象和樹節(jié)點(diǎn)的關(guān)聯(lián)問題,我們引入了用戶數(shù)據(jù)(UserData)對象的概念。你可以把用戶數(shù)據(jù)對象理解為一個(gè)輕量級的業(yè)務(wù)對象,它提供了足夠的信息可以用于標(biāo)識樹節(jié)點(diǎn)所關(guān)聯(lián)的業(yè)務(wù)對象,我們使用User Data替代Business Object關(guān)聯(lián)到樹節(jié)點(diǎn)上。一般來說,User Data和Business Object存在一對一的映射關(guān)系,我們可以使用Business Object的標(biāo)識符,或者使用Business Object的部分內(nèi)容來生成User Data。userData替代了Business Object綁定在樹節(jié)點(diǎn)上,或在網(wǎng)絡(luò)上傳輸,從而提升了組件的性能。當(dāng)然,在一些簡單的情況下,我們也可以直接把Business Object當(dāng)做User Data使用。
下面我們先來看xhtml頁面的源代碼:
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"xmlns:ajax="http://www.apusic.com/jsf/ajax" renderKitId="AJAX"xmlns:h="http://java.sun.com/jsf/html"><w:page title="動態(tài)樹"><w:head><w:stylesheet src="/common/resources/examples.css" /></w:head><layout:borderLayout fitToBody="true"><layout:panel region="north" header="false" height="50" border="false"><div class="examDesc"><p>本例演示動態(tài)樹,樹的節(jié)點(diǎn)由MB提供</p></div></layout:panel><layout:panel region="west" width="150" title="tree" split="true"><w:tree id="tree" border="false" style="height:100%;width:100%;" /></layout:panel><layout:panel region="center" title="message"><h:outputText id="response" escape="false"></h:outputText></layout:panel></layout:borderLayout></w:page></f:view>
![]() | 定義一個(gè)Tree。style和styleClass屬性可以用于設(shè)置Tree所在的容器風(fēng)格,這里我們使用到了style |
![]() | response字段綁定在頁面上的一個(gè)outputText上,我們通過給response設(shè)置值,向頁面打印Tree事件的信息 |
對應(yīng)的DynamicTreeBean的代碼如下:
package demo.tree;import org.operamasks.faces.annotation.Action;import org.operamasks.faces.annotation.Bind;import org.operamasks.faces.annotation.ManagedBean;import org.operamasks.faces.annotation.ManagedBeanScope;import org.operamasks.faces.component.tree.base.TreeDataProvider;import org.operamasks.faces.component.tree.impl.UITree;import org.operamasks.faces.component.tree.impl.UITreeNode;@ManagedBean(name = "DynamicTreeBean", scope = ManagedBeanScope.REQUEST)public class DynamicTreeBean {@Bindprivate UITree tree;@Bindprivate String response;@Bind(id = "tree")private TreeDataProvider treeData = new TreeDataProvider() {public Object[] getChildren(Object node) {if (node == null) {return new Object[] { "root" };}if (node == "root") {return new Object[] { "node1", "node2", "node3" };}if (node == "node1") {return new Object[] { "node11", "node12", "node13" };}if (node == "node2") {return new Object[] { "node21", "node22" };}if (node == "node3") {return new Object[] { "node31" };}return null;}public Boolean isChecked(Object node) {return !("root".equals(node));}public boolean isCascade(Object node) {return true;}public String getIcon(Object node) {return "";}public String getText(Object node) {return node.toString();}public String getHref(Object node) {return null;}public String getHrefTarget(Object node) {return null;}public boolean isExpanded(Object arg0) {return false;}};@Actionpublic void tree_onselect() {printTreeState();}@Actionpublic void tree_oncheck() {printTreeState();}@Actionpublic void tree_oncollapsenode() {printTreeState();}@Actionpublic void tree_onexpandnode() {printTreeState();}private void printTreeState() {if (tree == null) {return;}response = "當(dāng)前事件信息:<br/>事件名:" + tree.getEventName() + "<br/>";if (tree.getEventNode() != null) {response += "響應(yīng)事件的節(jié)點(diǎn)是:" + tree.getEventNode().getText();}if (tree.getCheckedNodes() != null) {response += "<br/><br/>當(dāng)前樹的狀態(tài)是:<br/>";response += "勾中的節(jié)點(diǎn)是:";for (UITreeNode node : tree.getCheckedNodes()) {response += node.getText() + ",";}}if (tree.getSelectedNode() != null) {response += "<br/>選中的節(jié)點(diǎn)是:";response += tree.getSelectedNode().getText();}}}
Jsf技術(shù)的一個(gè)優(yōu)勢就是Server端對組件的控制能力,OperaMasks Tree組件也提供了一組Server端接口,可以讓你編程控制組件的行為。下面我們通過一個(gè)示例來講解如何使用Tree組件的這種能力。該示例的效果圖如下:
示例實(shí)現(xiàn)右邊區(qū)域?qū)涔?jié)點(diǎn)的增加,刪除和修改。xhtml代碼如下:
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"xmlns:ajax="http://www.apusic.com/jsf/ajax" renderKitId="AJAX"xmlns:h="http://java.sun.com/jsf/html"><w:page title="增加,刪除,修改樹節(jié)點(diǎn)"><w:head><w:stylesheet src="/common/resources/examples.css" /></w:head><w:form><div class="examDesc"><p>本例演示動態(tài)樹,樹的節(jié)點(diǎn)添加、修改、刪除</p></div><layout:panelGrid columns="2"><layout:cell colspan="1" rowspan="1" valign="top"><w:tree id="tree" style="height:100%;width:100%;" width="200" height="400"autoScroll="true" border="true"><w:treeNode text="root" userData="root" icon="images/my_computer.gif"></w:treeNode></w:tree></layout:cell><layout:cell colspan="1" rowspan="1" valign="top"><layout:panelGrid columns="3" width="300"><layout:cell colspan="1" rowspan="1">文本:</layout:cell><layout:cell colspan="2" rowspan="1"><w:textField id="text"></w:textField></layout:cell><layout:cell colspan="1" rowspan="1">圖標(biāo):</layout:cell><layout:cell colspan="2" rowspan="1"><w:combo id="icon"><f:selectItem itemLabel="我的電腦" itemValue="images/my_computer.gif" /><f:selectItem itemLabel="文檔文件夾" itemValue="images/my_documents.gif" /><f:selectItem itemLabel="音樂文件夾" itemValue="images/my_music.gif" /><f:selectItem itemLabel="圖片文件夾" itemValue="images/my_pictures.gif" /><f:selectItem itemLabel="磁盤" itemValue="images/cd_drive.gif" /><f:selectItem itemLabel="圖片" itemValue="images/document_jpg.gif" /><f:selectItem itemLabel="音樂" itemValue="images/document_music.gif" /><f:selectItem itemLabel="文檔" itemValue="images/document_word.gif" /></w:combo></layout:cell><layout:cell colspan="1" rowspan="1">勾中:</layout:cell><layout:cell colspan="2" rowspan="1"><w:combo id="checked"><f:selectItem itemLabel="沒有勾選框" itemValue="none" /><f:selectItem itemLabel="勾中" itemValue="checked" /><f:selectItem itemLabel="不勾中" itemValue="unchecked" /></w:combo></layout:cell><layout:cell colspan="2" rowspan="1"><w:button value="增加" id="addNode" width="75" /><w:button value="刪除" id="deleteNode" width="75" /><w:button value="修改" id="modifyNode" width="75" /><w:button value="展開所有" id="expandAll" width="75" /><w:button value="收縮所有" id="collopseAll" width="75" /></layout:cell></layout:panelGrid></layout:cell></layout:panelGrid><h:outputText id="response" escape="false"></h:outputText></w:form></w:page></f:view>
后臺Java代碼如下:
package demo.tree;import org.operamasks.faces.annotation.Action;import org.operamasks.faces.annotation.Bind;import org.operamasks.faces.annotation.ManagedBean;import org.operamasks.faces.annotation.ManagedBeanScope;import org.operamasks.faces.component.tree.impl.UITree;import org.operamasks.faces.component.tree.impl.UITreeNode;@ManagedBean(name="DynamicTreeNodeBean", scope=ManagedBeanScope.REQUEST)public class DynamicTreeNodeBean {@Bindprivate UITree tree;@Bindprivate String text;@Bindprivate String icon;@Bindprivate String checked;@Bindprivate String response;@Actionpublic void tree_onselect(){UITreeNode node = tree.getSelectedNode();if(node != null){this.text = node.getText();this.icon = node.getIcon();setCheckState(node);}process();}@Actionpublic void addNode(){UITreeNode node = tree.getSelectedNode();if(node != null){UITreeNode newNode = new UITreeNode();newNode.setText(this.text);newNode.setIcon(this.icon);newNode.setChecked(getCheckState());node.add(newNode);process();}}@Actionpublic void deleteNode(){UITreeNode node = tree.getSelectedNode();if(node != null && node.getParent() instanceof UITreeNode){if(node != null){node.remove();}process();this.text = "";this.icon = "";this.checked = "";}}@Actionpublic void modifyNode(){UITreeNode node = tree.getSelectedNode();if(node != null){node.setText(this.text);node.setIcon(this.icon);node.setChecked(getCheckState());process();}}private Boolean getCheckState(){if("none".equals(this.checked)){return null;}if("checked".equals(this.checked)){return true;}if("unchecked".equals(this.checked)){return false;}return null;}private void setCheckState(UITreeNode node){if(node.getChecked() == null){this.checked = "none";}else if(Boolean.TRUE.equals(node.getChecked())){this.checked = "checked";}else if(Boolean.FALSE.equals(node.getChecked())){this.checked = "unchecked";}}@Actionpublic void tree_oncheck(){UITreeNode node = tree.getEventNode();if(node != null){setCheckState(node);process();}}private void process(){response = "";if(tree.getEventNode() != null){response += "響應(yīng)事件的節(jié)點(diǎn)是:" + tree.getEventNode().getText();response += "<br/>";}response += "勾中的節(jié)點(diǎn)是:";for(UITreeNode node : tree.getCheckedNodes()){response += node.getText() + ",";}response += "<br/>";if(tree.getSelectedNode() != null){response += "選中的節(jié)點(diǎn)是:" + tree.getSelectedNode().getText();}}@Actionpublic void collopseAll(){tree.collapseAll();}@Actionpublic void expandAll(){tree.expandAll();}}
1.本文所有示例均來源自Rich Component Demo
2.關(guān)于Annotation聲明ManagedBean和ManagedProperty的使用,請參考 Apusic OperaMasks文檔中心