OperaMasks提供了一个功能灵活而又简单易用的Tree作用组件,包括以下特性:
OperaMasks Tree的最简单的使用方式,是使用静态Tree节点。在一些场合下,这是满足需求的。让我们先看一下第一个示例的效果图: 该示例模拟一个Windows的Explorer,当用户使用鼠标操作树(单击、双击、展开和收缩树节点)时。显示接收到的事件的类型和影响到的节点。 所有示例的代码都分成两部分,一部分是页面代码,另一部分是服务器端ManagedBean代码。首先来看第一个示例的页面源码: <f:view xmlns="http://www./1999/xhtml" xmlns:f="http://java./jsf/core" xmlns:w="http://www./jsf/widget" xmlns:layout="http://www./jsf/layout" xmlns:ajax="http://www./jsf/ajax" renderKitId="AJAX" xmlns:h="http://java./jsf/html"> <w:page title="静态树"> <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>本例演示静态树</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> 页面对应的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 { @Bind private UITree tree; @Bind private String response; @Action public void tree_onselect(){ process(); } @Action public void tree_oncheck(){ process(); } private void process(){ response = "响应事件的节点是:" + tree.getEventNode().getText(); response += "<br/>"; response += "勾中的节点是:"; for(UITreeNode node : tree.getCheckedNodes()){ response += node.getText() + ","; } response += "<br/>"; response += "根节点的直接后代中半勾中的节点是:"; for (UITreeNode node : tree.getRootNode().getPartlyCheckedChildren()) { response += node.getText() + ",";; } response += "<br/>"; response += "全树范围内半勾中的节点是:"; for (UITreeNode node : tree.getPartlyCheckedNodes()) { response += node.getText() + ","; } response += "<br/>"; if(tree.getSelectedNode() != null){ response += "选中的节点是:" + tree.getSelectedNode().getText(); } } } 在Tree组件的实际应用中,更常见的是通过Server端程序,动态的装载树节点。OperaMasks Tree支持采用异步的、按需获取的方式装载数据。即只有在一个树节点需要被展开时,组件才通过Ajax的方式从服务端获取该节点的子节点数据。对于树节点的数量很大的情形,这种模式是灵活且高效的。 下面,我们将通过一个示例来展示Tree组件的动态能力,这个示例的效果图如下: 这个例子看上去似乎和示例一一样,其实不然,这个例子工作在动态数据装载模式下。 要能正确理解OperaMasks Tree的用法,需要解释一下业务对象(Business Object),用户数据对象(User Data Object)及它们之间的关系。一般来说,树的每一个节点都表示一个业务对象,比如Company,Department和User,我们用树的结构来表现这些业务对象之间的关联。把业务对象直接绑定到树节点上,无疑是直观的,但并非一个好的开发实践。原因是当业务对象是较重量级的大对象时,直接把业务对象绑定在树节点上,会导致性能方面的问题,这在Web环境下尤为严重。 为了解决业务对象和树节点的关联问题,我们引入了用户数据(UserData)对象的概念。你可以把用户数据对象理解为一个轻量级的业务对象,它提供了足够的信息可以用于标识树节点所关联的业务对象,我们使用User Data替代Business Object关联到树节点上。一般来说,User Data和Business Object存在一对一的映射关系,我们可以使用Business Object的标识符,或者使用Business Object的部分内容来生成User Data。userData替代了Business Object绑定在树节点上,或在网络上传输,从而提升了组件的性能。当然,在一些简单的情况下,我们也可以直接把Business Object当做User Data使用。 下面我们先来看xhtml页面的源代码: <f:view xmlns="http://www./1999/xhtml" xmlns:f="http://java./jsf/core" xmlns:w="http://www./jsf/widget" xmlns:layout="http://www./jsf/layout" xmlns:ajax="http://www./jsf/ajax" renderKitId="AJAX" xmlns:h="http://java./jsf/html"> <w:page title="动态树"> <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>本例演示动态树,树的节点由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>
对应的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 { @Bind private UITree tree; @Bind private 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; } }; @Action public void tree_onselect() { printTreeState(); } @Action public void tree_oncheck() { printTreeState(); } @Action public void tree_oncollapsenode() { printTreeState(); } @Action public void tree_onexpandnode() { printTreeState(); } private void printTreeState() { if (tree == null) { return; } response = "当前事件信息:<br/>事件名:" + tree.getEventName() + "<br/>"; if (tree.getEventNode() != null) { response += "响应事件的节点是:" + tree.getEventNode().getText(); } if (tree.getCheckedNodes() != null) { response += "<br/><br/>当前树的状态是:<br/>"; response += "勾中的节点是:"; for (UITreeNode node : tree.getCheckedNodes()) { response += node.getText() + ","; } } if (tree.getSelectedNode() != null) { response += "<br/>选中的节点是:"; response += tree.getSelectedNode().getText(); } } } Jsf技术的一个优势就是Server端对组件的控制能力,OperaMasks Tree组件也提供了一组Server端接口,可以让你编程控制组件的行为。下面我们通过一个示例来讲解如何使用Tree组件的这种能力。该示例的效果图如下: 示例实现右边区域对树节点的增加,删除和修改。xhtml代码如下: <f:view xmlns="http://www./1999/xhtml" xmlns:f="http://java./jsf/core" xmlns:w="http://www./jsf/widget" xmlns:layout="http://www./jsf/layout" xmlns:ajax="http://www./jsf/ajax" renderKitId="AJAX" xmlns:h="http://java./jsf/html"> <w:page title="增加,删除,修改树节点"> <w:head> <w:stylesheet src="/common/resources/examples.css" /> </w:head> <w:form> <div class="examDesc"> <p>本例演示动态树,树的节点添加、修改、删除</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">图标:</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 { @Bind private UITree tree; @Bind private String text; @Bind private String icon; @Bind private String checked; @Bind private String response; @Action public void tree_onselect(){ UITreeNode node = tree.getSelectedNode(); if(node != null){ this.text = node.getText(); this.icon = node.getIcon(); setCheckState(node); } process(); } @Action public 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(); } } @Action public 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 = ""; } } @Action public 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"; } } @Action public void tree_oncheck(){ UITreeNode node = tree.getEventNode(); if(node != null){ setCheckState(node); process(); } } private void process(){ response = ""; if(tree.getEventNode() != null){ response += "响应事件的节点是:" + tree.getEventNode().getText(); response += "<br/>"; } response += "勾中的节点是:"; for(UITreeNode node : tree.getCheckedNodes()){ response += node.getText() + ","; } response += "<br/>"; if(tree.getSelectedNode() != null){ response += "选中的节点是:" + tree.getSelectedNode().getText(); } } @Action public void collopseAll(){ tree.collapseAll(); } @Action public void expandAll(){ tree.expandAll(); } } 1.本文所有示例均来源自Rich Component Demo 2.关于Annotation声明ManagedBean和ManagedProperty的使用,请参考 Apusic OperaMasks文档中心
|
|