分享

AOM2.0中Tree组件之一:定义Tree

 孤独求学者 2011-03-10

1. 概述

OperaMasks提供了一个功能灵活而又简单易用的Tree作用组件,包括以下特性:

  • 支持静态定义树节点,也支持从服务器端异步装载节点数据

  • 提供Ajax方式的事件监听器,可监听onclick,oncheck,onselect、onexpandnode和oncollapsenode事件

  • 可定制树节点的显示,例如text和icon,也可以控制节点前面是否有选择框

  • 提供了一组服务器端API,可编程控制树的行为

    这篇文章将通过三个简单示例,向您介绍如何使用OperaMasks Tree组件。

2. 使用静态树节点

OperaMasks Tree的最简单的使用方式,是使用静态Tree节点。在一些场合下,这是满足需求的。让我们先看一下第一个示例的效果图:

示例一

Figure 1. 示例一


该示例模拟一个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">
1<w:tree id="tree" loadAllNodes="true" border="false" rootVisible="true"
expandAll="true" style="width:100%;height:100%;">
2<w:treeNode text="root" icon="" checked="true">
3<w:treeNode text="node1" icon="" checked="true">
4<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">
5<h:outputText id="response" escape="false"></h:outputText>
</layout:panel>
</layout:borderLayout>
</w:page>
</f:view>
1

tree标签用于定义一个Tree组件

2

用treeNode标签定义树的根节点root,其它树节点都包含在这个节点中。text为节点的文本显示,icon指定了节点显示的图标,checked="true"表示这个节点的选择框被选中

3

node1节点,是root节点的子节点。treeNode标签使用嵌套的方式表示树的结构,node1被包含在root中

4

node11节点,node1的子节点

5

定义了一个输出文本,用于显示Tree的事件消息

页面对应的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(){
1response = "响应事件的节点是:"  + 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();
}
}
}
1

response字段绑定在页面上的一个outputText上,我们通过给response设置值,向页面打印Tree事件的信息

3. 动态装载树节点

在Tree组件的实际应用中,更常见的是通过Server端程序,动态的装载树节点。OperaMasks Tree支持采用异步的、按需获取的方式装载数据。即只有在一个树节点需要被展开时,组件才通过Ajax的方式从服务端获取该节点的子节点数据。对于树节点的数量很大的情形,这种模式是灵活且高效的。

下面,我们将通过一个示例来展示Tree组件的动态能力,这个示例的效果图如下:

示例二

Figure 2. 示例二


这个例子看上去似乎和示例一一样,其实不然,这个例子工作在动态数据装载模式下。

要能正确理解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">
1<w:tree id="tree" border="false" style="height:100%;width:100%;" />
</layout:panel>
<layout:panel region="center" title="message">
2<h:outputText id="response" escape="false"></h:outputText>
</layout:panel>
</layout:borderLayout>
</w:page>
</f:view>
1

定义一个Tree。style和styleClass属性可以用于设置Tree所在的容器风格,这里我们使用到了style

2

response字段绑定在页面上的一个outputText上,我们通过给response设置值,向页面打印Tree事件的信息

对应的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")
1private 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
2public 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;
}
3response = "当前事件信息:<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();
}
}
}
1

tree的构造

2

选中树结点tree_onselect事件

3

response字段绑定在页面上的一个outputText上,我们通过给response设置值,向页面打印Tree事件的信息

4. 使用服务器端API

Jsf技术的一个优势就是Server端对组件的控制能力,OperaMasks Tree组件也提供了一组Server端接口,可以让你编程控制组件的行为。下面我们通过一个示例来讲解如何使用Tree组件的这种能力。该示例的效果图如下:

示例三

Figure 3. 示例三


示例实现右边区域对树节点的增加,删除和修改。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">
1<w:tree id="tree" style="height:100%;width:100%;" width="200" height="400"
autoScroll="true" border="true">
2<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">
3<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>
1

定义树

2

树的根节点root

3

树结点的增加按钮

后台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
1public void tree_onselect(){
UITreeNode node = tree.getSelectedNode();
if(node != null){
this.text = node.getText();
this.icon = node.getIcon();
setCheckState(node);
}
process();
}
@Action
2public 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;
}
3private 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(){
4response = "";
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

tree_onselect,树结点的选中事件

2

点击按钮,根据选择的节点属性设置增加相应的树结点

3

设置节点的属性,此处为CheckState,节点前的勾选框是否选中

4

response字段绑定在页面上的一个outputText上,我们通过给response设置值,向页面打印Tree事件的信息

5. 参考

1.本文所有示例均来源自Rich Component Demo

2.关于Annotation声明ManagedBean和ManagedProperty的使用,请参考 Apusic OperaMasks文档中心

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多