前段时间,因为公司分配了这样的任务,开始研究了richfaces3.3.1的demo。其中关于树的拖动部分,通过研究demo的例子,我做了一些修改,实现了下面的功能:
1.树的动态加载;
2.一棵树内的节点的拖动,只能从叶子节点拖动到非叶子节点。
不过,我想实现更复杂的拖动树在这基础上修改也不会太难。
下面贴代码吧:
/pages/tree/下的jsp:
<%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> <%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@taglib uri="http://richfaces.org/rich" prefix="rich"%> <%@taglib uri="http://richfaces.org/a4j" prefix="a4j"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>tree demo</title> </head> <style> .col1, .col2 { width:50%; vertical-align:top; } .rich-table-cell, .rich-table{ border:none; } </style> <body> <f:view> <div id="top"> <h3>tree demo about drag and drop</h3> </div> <rich:dragIndicator id="indicator1" /> <h:form> <h:panelGrid columns="1" width="100%" columnClasses="col1"> <rich:tree style="width:300px" switchType="ajax" value="#{treesBean.treeNode}" var="item" id="tree" treeNodeVar="treeNode" nodeFace="#{item.type == 1 ? 'node' : 'leaf'}"> <rich:treeNode type="node" acceptedTypes="all" dropListener="#{evntBean.processDrop}" dropValue="#{treeNode}" icon="#{treeNode.icon}" iconLeaf="#{treeNode.leafIcon}" changeExpandListener="#{treesBean.processExpansion}" data="#{treeNode}" reRender="tree"> <h:outputText value="#{item.nodeName}"/> </rich:treeNode> <rich:treeNode type="leaf" dragType="all" dragValue="#{treeNode}" icon="#{treeNode.icon}" iconLeaf="#{treeNode.leafIcon}" dragIndicator="indicator1" dragListener="#{evntBean.processDrag}"> <rich:dndParam name="label" type="drag" value="#{item.nodeName}"></rich:dndParam> <h:outputText value="#{item.nodeName}"/> </rich:treeNode> </rich:tree> </h:panelGrid> </h:form> </f:view> </body> </html>
package demo下的hibernate.cfg.xml:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/richfaces_demo</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">mysql51</property> <property name="hibernate.connection.pool_size">10</property> <property name="show_sql">true</property> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <mapping resource="/demo/grid/Person.hbm.xml"/> <mapping resource="/demo/tree/NodeVo.hbm.xml"/> </session-factory> </hibernate-configuration>
package demo.tree下的class和*.hbm.xml:
package demo.tree; import org.richfaces.model.TreeNodeImpl; public class DemoTreeNodeImpl extends TreeNodeImpl{ private String icon;//节点icon private String leafIcon;//叶子节点icon private int nodeId;//节点id private int type;//节点类型:1-节点 2-叶子节点 private int status;//节点的状态: 1-展开状态 0-折叠状态 public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public String getLeafIcon() { return leafIcon; } public void setLeafIcon(String leafIcon) { this.leafIcon = leafIcon; } public int getNodeId() { return nodeId; } public void setNodeId(int nodeId) { this.nodeId = nodeId; } public int getType() { return type; } public void setType(int type) { this.type = type; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } }
package demo.tree; import org.richfaces.component.Dropzone; import org.richfaces.component.Draggable; import org.richfaces.event.DropEvent; import org.richfaces.event.DragEvent; import org.richfaces.event.DropListener; import org.richfaces.event.DragListener; import org.richfaces.component.UITree; import org.richfaces.component.UITreeNode; import org.ajax4jsf.context.AjaxContext; public class EventBean implements DropListener,DragListener{//,NodeExpandedListener{ private TreesBean treesBean; private Object dragValue; /** * 处理树内子节点拖动(到节点)操作 */ public void processDrop(DropEvent dropEvent) { Dropzone dropzone = (Dropzone) dropEvent.getComponent(); treesBean.move(dragValue, dropzone.getDropValue()); UITree destTree = null; if(dropEvent.getSource() instanceof UITreeNode){ UITreeNode destNode = (UITreeNode)dropEvent.getSource(); destTree = destNode.getUITree(); }else if(dropEvent.getSource() instanceof UITree){ destTree = (UITree)dropEvent.getSource(); } AjaxContext ac = AjaxContext.getCurrentInstance(); // Add destination tree to reRender try { ac.addComponentToAjaxRender(destTree); } catch (Exception e) { System.err.print("ac.addComponentToAjaxRender(destTree):" + e.getMessage()); } } public void processDrag(DragEvent dragEvent){ Draggable draggable = (Draggable)dragEvent.getComponent(); dragValue = draggable.getDragValue(); } public TreesBean getTreesBean() { return treesBean; } public void setTreesBean(TreesBean treesBean) { this.treesBean = treesBean; } }
/* * NodeVo.java * pojo类 hibernate entitybean */ package demo.tree; public class NodeVo{ private int id; private int nodeId;//节点id private String nodeName;//节点名 private int parentNodeId;//父节点id private int type;//节点类型:1-节点 0-子节点 private String icon;//节点icon private String leafIcon;//子节点icon /** * Creates a new instance of NodeVo */ public NodeVo() { } /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } /** * @return the nodeId */ public int getNodeId() { return nodeId; } /** * @param nodeId the nodeId to set */ public void setNodeId(int nodeId) { this.nodeId = nodeId; } /** * @return the nodeName */ public String getNodeName() { return nodeName; } /** * @param nodeName the nodeName to set */ public void setNodeName(String nodeName) { this.nodeName = nodeName; } /** * @return the parentNodeId */ public int getParentNodeId() { return parentNodeId; } /** * @param parentNodeId the parentNodeId to set */ public void setParentNodeId(int parentNodeId) { this.parentNodeId = parentNodeId; } /** * @return the type */ public int getType() { return type; } /** * @param type the type to set */ public void setType(int type) { this.type = type; } /** * @return the icon */ public String getIcon() { return icon; } /** * @param icon the icon to set */ public void setIcon(String icon) { this.icon = icon; } /** * @return the leafIcon */ public String getLeafIcon() { return leafIcon; } /** * @param leafIcon the leafIcon to set */ public void setLeafIcon(String leafIcon) { this.leafIcon = leafIcon; } }
package demo.tree; import java.util.List; import org.richfaces.model.TreeNode; import org.richfaces.event.NodeExpandedEvent; import org.richfaces.component.html.HtmlTreeNode; import javax.faces.context.FacesContext; import org.hibernate.Session; import demo.util.DBUtil; public class TreesBean { private DemoTreeNodeImpl rootNode = null; private static final String ICONS_PATH = "/images/tree/dnd/"; public TreesBean(){ loadTree(1000);//load tree which root node Id is 1000 } /** * 为节点node加载第一层子节点 */ private void addNodes(DemoTreeNodeImpl node) { String sql = "from NodeVo where parentNodeId=:nodeId"; Session ss = DBUtil.getSf().openSession(); List list = ss.createQuery(sql).setInteger("nodeId", node.getNodeId()).list(); ss.close(); node.removeChild("fakeNode"); if(null != list && list.size() > 0){ for(int i=0;i<list.size();i++){ NodeVo o = (NodeVo)list.get(i); DemoTreeNodeImpl tmpNode = new DemoTreeNodeImpl(); tmpNode.setNodeId(o.getNodeId()); tmpNode.setType(o.getType()); tmpNode.setData(o); if(o.getType() == 1){//node if("Unsorted".equalsIgnoreCase(o.getNodeName())){ tmpNode.setIcon(ICONS_PATH + "image.gif"); tmpNode.setLeafIcon(ICONS_PATH + "image.gif"); }else if("Favorites".equalsIgnoreCase(o.getNodeName())){ tmpNode.setIcon(ICONS_PATH + "favorites.gif"); tmpNode.setLeafIcon(ICONS_PATH + "favorites.gif"); }else if("Trash".equalsIgnoreCase(o.getNodeName())){ tmpNode.setIcon(ICONS_PATH + "trash.gif"); tmpNode.setLeafIcon(ICONS_PATH + "trash.gif"); }else{ tmpNode.setIcon(ICONS_PATH + "iconFolder.gif"); tmpNode.setLeafIcon(ICONS_PATH + "iconFolder.gif"); } }else if(o.getType() == 0){//leaf tmpNode.setLeafIcon(ICONS_PATH + "disc.gif"); } tmpNode.setParent(node); node.addChild(new Integer(tmpNode.getNodeId()), tmpNode); if(tmpNode.getType() == 1){ //构造一个假的的子结点 DemoTreeNodeImpl fakeNode = new DemoTreeNodeImpl(); NodeVo vo = new NodeVo(); vo.setNodeName("loading..."); vo.setType(1); fakeNode.setData(vo); fakeNode.setIcon(ICONS_PATH + "loading.gif"); fakeNode.setLeafIcon(ICONS_PATH + "loading.gif"); fakeNode.setType(1); tmpNode.addChild("fakeNode",fakeNode); } } } } /** * 加载节点id为nodeId的树 */ private void loadTree(int nodeId) { Session ss = DBUtil.getSf().openSession(); String sql = "from NodeVo where parentNodeId = 0 and nodeId=:nodeId"; Object o = ss.createQuery(sql).setInteger("nodeId", nodeId).uniqueResult(); ss.close(); if (null != o) { NodeVo nodeVo = (NodeVo) o; rootNode = new DemoTreeNodeImpl(); rootNode.setNodeId(nodeVo.getNodeId()); rootNode.setData(nodeVo); rootNode.setParent(null); addNodes(rootNode); } } /** * 拖动树的叶子节点到节点 */ public void move(Object dragObj,Object dropObj){ DemoTreeNodeImpl dragNode = (DemoTreeNodeImpl)dragObj; DemoTreeNodeImpl dropNode = (DemoTreeNodeImpl)dropObj; TreeNode dragParentNode = dragNode.getParent(); if(null != dragParentNode){ if(((DemoTreeNodeImpl)dragParentNode).getNodeId() == dropNode.getNodeId()){ //在父结点内拖动无操作 return; } } String sql = "from NodeVo where nodeId=:nodeId"; Session ss = DBUtil.getSf().openSession(); Object o = ss.createQuery(sql).setInteger("nodeId",dragNode.getNodeId()).uniqueResult(); if(null != o){ NodeVo nodeVo = (NodeVo)o; nodeVo.setParentNodeId(dropNode.getNodeId()); ss.beginTransaction().begin(); ss.update(nodeVo); ss.beginTransaction().commit(); ss.flush(); } ss.close(); NodeVo vo = (NodeVo)dragNode.getData(); vo.setParentNodeId(dropNode.getNodeId()); dragNode.setData(vo); dragParentNode.removeChild(new Integer(dragNode.getNodeId()));//这里必须先做删除操作,之后再做setParent()和addChild()操作,这与richfaces的具体实现有关 dragNode.setParent(dropNode); dropNode.addChild(new Integer(dragNode.getNodeId()),dragNode); FacesContext.getCurrentInstance().renderResponse(); } /** * 处理展开与折叠操作 */ public void processExpansion(NodeExpandedEvent e){ HtmlTreeNode tNode = (HtmlTreeNode)e.getComponent(); DemoTreeNodeImpl o = (DemoTreeNodeImpl)tNode.getData(); if(o.getStatus() == 0){//当该结点处于折叠状态下,则加载该结点的第一次子结点 addNodes(o); o.setStatus(1); } } public DemoTreeNodeImpl getTreeNode() { return rootNode; } public void setTreeNode(DemoTreeNodeImpl rootNode){ this.rootNode = rootNode; } }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="demo.tree"> <class name="NodeVo" table="tree"> <id column="id" name="id"> <generator class="native"/> </id> <property name="nodeId" not-null="true"/> <property name="nodeName" not-null="true" type="java.lang.String"/> <property name="parentNodeId" not-null="true"/> <property name="type" not-null="true"/> </class> </hibernate-mapping>
package demo.util下的class:
package demo.util; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.hibernate.cfg.Configuration; import org.hibernate.SessionFactory; import javax.servlet.ServletContextListener; /** * * @author SailingLee */ public class DBUtil implements ServletContextListener{ private static SessionFactory sf = null; public DBUtil() { } public void contextInitialized(ServletContextEvent servletContextEvent) { sf = new Configuration().configure("/demo/hibernate.cfg.xml").buildSessionFactory(); } public void contextDestroyed(ServletContextEvent servletContextEvent) { sf.close(); } public static SessionFactory getSf() { return sf; } }
package demo.phase下的class:
package demo.phase; import java.util.Map; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.event.PhaseEvent; import javax.faces.event.PhaseId; import javax.faces.event.PhaseListener; public class PostbackPhaseListener implements PhaseListener { public static final String POSTBACK_ATTRIBUTE_NAME = PostbackPhaseListener.class.getName(); public void afterPhase(PhaseEvent event) { } public void beforePhase(PhaseEvent event) { FacesContext facesContext = event.getFacesContext(); Map requestMap = facesContext.getExternalContext().getRequestMap(); requestMap.put(POSTBACK_ATTRIBUTE_NAME, Boolean.TRUE); } public PhaseId getPhaseId() { return PhaseId.APPLY_REQUEST_VALUES; } public static boolean isPostback() { FacesContext facesContext = FacesContext.getCurrentInstance(); if (facesContext != null) { ExternalContext externalContext = facesContext.getExternalContext(); if (externalContext != null) { return Boolean.TRUE.equals( externalContext.getRequestMap().get(POSTBACK_ATTRIBUTE_NAME)); } } return false; } }
/WEB-INF/下的xml:
<?xml version='1.0' encoding='UTF-8'?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <!-- =========== FULL CONFIGURATION FILE ================================== --> <faces-config> <!--(down) add by SailingLee about tree demo--> <managed-bean> <managed-bean-name>treesBean</managed-bean-name> <managed-bean-class>demo.tree.TreesBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>evntBean</managed-bean-name> <managed-bean-class>demo.tree.EventBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>treesBean</property-name> <property-class>demo.tree.TreesBean</property-class> <value>#{treesBean}</value> </managed-property> </managed-bean> <!--(up) add by SailingLee about tree demo--> <factory> <application-factory>org.richfaces.ui.application.StateApplicationFactory</application-factory> </factory> <lifecycle> <phase-listener>demo.phase.PostbackPhaseListener</phase-listener> </lifecycle> </faces-config>
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- RichFaces Skin --> <context-param> <param-name>org.richfaces.SKIN</param-name> <param-value>blueSky</param-value> </context-param> <!-- Faces Servlet --> <context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>-1</load-on-startup> </servlet> <!-- Faces Servlet Mapping --> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> <!-- RichFaces Filter --> <filter> <display-name>RichFaces Filter</display-name> <filter-name>richfaces</filter-name> <filter-class>org.ajax4jsf.Filter</filter-class> </filter> <filter-mapping> <filter-name>richfaces</filter-name> <servlet-name>Faces Servlet</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> <welcome-file> index.jsp </welcome-file> </welcome-file-list> <listener> <listener-class>demo.util.DBUtil</listener-class> </listener> </web-app>
用到的类库见图片附件lib.jpg :)