import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; //JTree应用示例:简单日志管理系统 public class BlogManagerUIV2 extends javax.swing.JFrame { //主界面:初始化时创建,因为要在内部类中使用,要定义为final型 private static final BlogManagerUIV2 mainUI=new BlogManagerUIV2(); public static void main(String[] args) { mainUI.init(); } //初始化界面内容 public void init() { this.setTitle("东方标准JTree--日志管理v2"); this.setSize(300, 400); java.awt.FlowLayout fl = new java.awt.FlowLayout(0); this.setLayout(fl); //调用模拟生成数据的对象方法,得到UserInfo对象列表 UserDao dao=new UserDao(); List<UserInfo> userList =dao.getAllUser(); //根据传入的用户列表对象,调用创建树的方法,在createTree方法中创建树上的节点 javax.swing.JTree tree = createTree(userList); //设定树上的弹出菜单,并在createPopMenu方法中实现菜单事件 javax.swing.JPopupMenu pop=createPopMenu(tree); tree.setComponentPopupMenu(pop);//指定树上的弹出菜单 this.add(tree);//将创建好的树加到界面上 this.setDefaultCloseOperation(3); this.setVisible(true); } /** * 根据传入的UserInfo对象列表创建树上的节点 * 1.列表中每个UserInfo对象是一个枝节点; * 2.每个UserInfo对象对象的多个UserBlog对象为其叶节点 * @param userList:装有UserInfo对象的列表 * @return :创建好的树 */ public javax.swing.JTree createTree(List<UserInfo> userList) { final javax.swing.JTree tree = new javax.swing.JTree();// 创建默认树 // 首先,创建一个根节点: javax.swing.tree.DefaultMutableTreeNode rootNode = new javax.swing.tree.DefaultMutableTreeNode(); // 设定节点上的数据对象,节点显示标题则为设定对象的toString()值 rootNode.setUserObject("日志管理"); //取得要加载到树上的用户对象列表 for (int i = 0; i <userList.size(); i++) { DefaultMutableTreeNode teamNode = new DefaultMutableTreeNode(); UserInfo us=userList.get(i); //得到列表中每一个用户对象,设为树节点对象 teamNode.setUserObject(us); // 将组节点加到根节点上 rootNode.add(teamNode); //取得所属于这个用户的blog对象列表 List<UserBlog> blogList=us.getBlogList(); for (int t = 0; t <blogList.size(); t++) { DefaultMutableTreeNode userNode = new DefaultMutableTreeNode(); UserBlog ub=blogList.get(t);//从队列中得到blog对象 userNode.setUserObject(ub); teamNode.add(userNode); // 将用户节点加到组节点上 } // 创建树的Model对象,创建时传入根节点: javax.swing.tree.DefaultTreeModel dm = new DefaultTreeModel(rootNode); // 将模型设给树,树上显示的将上前面所加载的节点 tree.setModel(dm); } return tree; } /** * 创建传入的JTree对象上的弹出菜单,并指定事件处理 * @param tree:弹出菜单所在的树对象 * @return:创建好的弹出菜单对象 */ private javax.swing.JPopupMenu createPopMenu(final JTree tree){ //创建弹出菜单对象 javax.swing.JPopupMenu popMenu=new javax.swing.JPopupMenu(); //1创建文件菜单下的菜单项: javax.swing.JMenuItem mi_open=new javax.swing.JMenuItem("添加"); mi_open.setActionCommand("add");//设置菜单的命令关键字 javax.swing.JMenuItem mi_new=new javax.swing.JMenuItem("删除"); mi_new.setActionCommand("del"); javax.swing.JMenuItem mi_exit=new javax.swing.JMenuItem("修改"); mi_exit.setActionCommand("mod"); //创建内部类的菜单事件监听器对象: java.awt.event.ActionListener ac_listener=new java.awt.event.ActionListener(){ public void actionPerformed(ActionEvent e){ //当事件发生时,调用事件处理方法 treeMenuAction(e,tree); } }; //2 给菜单项加上事件监听器: mi_open.addActionListener(ac_listener); mi_new.addActionListener(ac_listener); mi_exit.addActionListener(ac_listener); //3 将菜单项加到弹出菜单对象上: popMenu.add(mi_open); popMenu.add(mi_new); popMenu.add(mi_exit); return popMenu; } /** * 响应树上的弹出菜单事件 * 1.当点击菜单时选中的树节点是代表UserInfo对象时调用changeUserInfo方法 * 2.当点击菜单时选中的树节点是代表BlogInfo对象时调用chageBlogInfo方法 * @param e :事件对象 * @param tree :事件发生所在的树 */ private void treeMenuAction(ActionEvent e,JTree tree){ String command=e.getActionCommand();//得到选中菜单的命令 //得到在树上选中的路径 javax.swing.tree.TreePath tp=tree.getSelectionPath(); //如果选中了树上的某个节点: if(null!=tp){ ///得到选中的节点,每个节点都是DefaultMutableTreeNode对象 DefaultMutableTreeNode selectNode=(DefaultMutableTreeNode)tp.getLastPathComponent(); //取得选中节点内的对象,即setUserObject传入的对象 Object userObject=selectNode.getUserObject(); //判断是何种对象 if(userObject instanceof UserInfo){//选中用户节点 UserInfo user=(UserInfo)userObject;//强制转型 //调用处理方法,传入命令字和要处理的对象 changeUserInfo(command,user,selectNode); } if(userObject instanceof UserBlog){//选中日志节点 UserBlog blog=(UserBlog)userObject;//强制转型 //调用处理方法,传处命令字和要处理的对象 chageBlogInfo(command,blog,selectNode); } }else{ javax.swing.JOptionPane.showMessageDialog(this,"请选中树上的节点!"); } } //处理对树上UserInfo节点选中的事件 private void changeUserInfo(String command,UserInfo user,DefaultMutableTreeNode selectNode){ if(command.equals("del")){ //让用户确认 int i=javax.swing.JOptionPane.showConfirmDialog(this, "确认要移除吗?"); if(i==0){//确认框返架结果如为0,表示确认del selectNode.removeFromParent(); javax.swing.SwingUtilities.updateComponentTreeUI(this); } } else if(command.equals("add")||command.equals("mod")){ showAddOrModifyUserInfoDialog(command,user,selectNode); } } //处理树上UserBlog对象选中的事件 private void chageBlogInfo(String command,UserBlog blog,DefaultMutableTreeNode selectNode){ if(command.equals("del")){ //让用户确认 int i=javax.swing.JOptionPane.showConfirmDialog(this, "确认要移除吗?"); if(i==0){//确认框返架结果如为0,表示确认del selectNode.removeFromParent(); javax.swing.SwingUtilities.updateComponentTreeUI(this); } } else if(command.equals("add")||command.equals("mod")){ showAddOrModifyBlogDialog(command,blog,selectNode); } } //显示添加/修改blog的对话话,并实现事件响应 private void showAddOrModifyBlogDialog(final String command,final UserBlog blog,final DefaultMutableTreeNode selectNode){ //创建并弹出对话框,显示输入组件, final javax.swing.JDialog jda=new javax.swing.JDialog(); java.awt.FlowLayout fl=new java.awt.FlowLayout(); jda.setLayout(fl); jda.setSize(this.getWidth(),this.getHeight()/2); jda.setModal(true); //设置为"模型"对话框 javax.swing.JLabel la_title=new javax.swing.JLabel("日志标题:"); final javax.swing.JTextField jta_title=new javax.swing.JTextField(15); javax.swing.JLabel la_content=new javax.swing.JLabel("日志内容:"); final javax.swing.JTextArea jta_content=new javax.swing.JTextArea(5,15); jda.add(la_title); jda.add(jta_title); jda.add(la_content); jda.add(jta_content); javax.swing.JButton bu_blog=new javax.swing.JButton(); //根据不同的操作,设定对话框按钮上的标签 if(command.equals("add")){ jda.setTitle("用户"+blog.getUserinfo().getName()+"添加一篇日志"); bu_blog.setText("添加"); }else if(command.equals("mod")){ jda.setTitle("修改日志"); bu_blog.setText("修改"); //将节点中所含对象的信息显示到界面上: jta_title.setText(blog.getTitle()); jta_content.append(blog.getContent()); } jda.add(bu_blog); 添加对话框上的按钮的事件处理器 bu_blog.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ String title=jta_title.getText();//得到用户输入的内容 String content=jta_content.getText(); if(command.equals("add")){//如果是添加命令 UserBlog nb=new UserBlog();//根据输入内容创建blog对象 nb.setId(1); nb.setTitle(title); nb.setContent(content); nb.setUserinfo(blog.getUserinfo()); blog.getUserinfo().addBlog(nb);//加给对应的用户对象内 //创建新节点 DefaultMutableTreeNode dm=new DefaultMutableTreeNode(); dm.setUserObject(nb); //得到己选中节点的父节点,将根据用户输入新建的节点加到树上: DefaultMutableTreeNode parent=(DefaultMutableTreeNode)selectNode.getParent(); parent.add(dm); } if(command.equals("mod")){//如果是修改命令: //重新设定原来选中的节点所代表的blog对象的标题和内容: blog.setTitle(title); blog.setContent(content); } jda.dispose();//关闭对话框 //刷新界面,否则树上显示不了! javax.swing.SwingUtilities.updateComponentTreeUI(mainUI); } }); jda.setVisible(true); } //显示添加/修改UserInfo的对话话,并实现事件响应 private void showAddOrModifyUserInfoDialog(final String command,final UserInfo user,final DefaultMutableTreeNode selectNode){ //修改或新建时,弹出一个Jdialog(对话框,类似于JFrame)对象 final javax.swing.JDialog jda=new javax.swing.JDialog(); java.awt.FlowLayout fl=new java.awt.FlowLayout(); jda.setLayout(fl); jda.setSize(this.getWidth(),this.getHeight()/2); jda.setModal(true); javax.swing.JLabel la_name=new javax.swing.JLabel("用户名:"); final javax.swing.JTextField jta_name=new javax.swing.JTextField(15); javax.swing.JLabel la_age=new javax.swing.JLabel("用户年令:"); final javax.swing.JTextField jta_age=new javax.swing.JTextField(5); jda.add(la_name); jda.add(jta_name); jda.add(la_age); jda.add(jta_age); javax.swing.JButton bu_blog=new javax.swing.JButton(); if(command.equals("add")){//如果是添加命令 jda.setTitle("添加一个用户信息"); bu_blog.setText("添加"); }else if(command.equals("mod")){ jda.setTitle("修改用户信息"); bu_blog.setText("修改"); //将节点中所含对象的信息显示到界面上: jta_name.setText(user.getName()); jta_age.setText(""+user.getAge());//用户年令是设定到界面上必须是字符 } jda.add(bu_blog); bu_blog.addActionListener(new ActionListener(){//添加对话框上的事件执行 public void actionPerformed(ActionEvent e){ String name=jta_name.getText();//得到用户输入的内容 String str_age=jta_age.getText(); //将从界面上得到的年令串转为int型: int age=Integer.parseInt(str_age); if(command.equals("add")){//如果是添加命令 UserInfo newUser=new UserInfo();//根据输入内容创建blog对象 newUser.setId(1); newUser.setAge(age); newUser.setName(name); //创建新节点,代表新建的用户对象 DefaultMutableTreeNode newUserNode=new DefaultMutableTreeNode(); newUserNode.setUserObject(newUser); //得到己选中节点的父节点,将根据用户输入新建的节点加到树上: DefaultMutableTreeNode parent=(DefaultMutableTreeNode)selectNode.getParent(); parent.add(newUserNode); //第一个新建的用户,要为其建一个blog对象 UserBlog nb=new UserBlog(); nb.setId(1); nb.setTitle("默认日志"); nb.setContent("默认内容"); nb.setUserinfo(newUser); newUser.addBlog(nb);//加给对应的用户对象内 //创建新节点 DefaultMutableTreeNode dmBlog=new DefaultMutableTreeNode(); dmBlog.setUserObject(nb); //将给这个新用户默认的新日志对象做为一个节点加到新用户对象代表的节点下: newUserNode.add(dmBlog); } if(command.equals("mod")){//如果是修改命令: //重新设定原来选中的节点所代表的UserInfo对象的标题和内容: user.setAge(age); user.setName(name); } jda.dispose();//处理完毕后,关闭对话框 //刷新界面,否则树上显示不了! javax.swing.SwingUtilities.updateComponentTreeUI(mainUI); } }); jda.setVisible(true); } } 5.完善分析 必须明白的一个问题是:简单的功能是谁都可以实现的,但精致、完美是无至境的。上面的示例实现的简单的功能,但还有很大缺陷: 1. 用户的输入没有较验,如新建UserInfo对象时,如用户不输出信息,就点了保存按钮,程序就出会错而没有给用户友好的提示。这点我想你可以自己完成! 2. 界面上通常需要用户输入固定格式的内容,如年令必须是数字---我们编程时可以指定这一点---如果程序给了用户犯错的机会,那是程序员的错! 如下代码示例如何格式化输入:
public static void main(String args[]) { JFrame f = new JFrame("格式化输出测试"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); java.awt.FlowLayout fl = new java.awt.FlowLayout(); f.setLayout(fl); // 日期格式输入 DateFormat format = new SimpleDateFormat("yyyy年MM月dd日"); DateFormatter df = new DateFormatter(format); final JFormattedTextField ft_date = new JFormattedTextField(df); ft_date.setValue(new Date()); f.add(ft_date); MaskFormatter ip_mf = null; // IP地址(都是数字)格式输入 MaskFormatter ip_age = null; // 年令格式 try { ip_mf = new MaskFormatter("###-###-###-###"); ip_mf.setPlaceholderCharacter('_');// 加上占位符 ip_age = new MaskFormatter(" ## "); } catch (Exception ef) { ef.printStackTrace(); } final JFormattedTextField ft_ipAdd = new JFormattedTextField(ip_mf); f.add(ft_ipAdd); //输入数字,做为年令 final JFormattedTextField ft_age = new JFormattedTextField(ip_age); f.add(ft_age); // 创建一个事件监听器 ActionListener acton = new ActionListener() { public void actionPerformed(ActionEvent e) { String date = ft_date.getText(); String ipadd = ft_ipAdd.getText(); //得到年令输入框中的值,并去掉前后的空格 int age=Integer.parseInt(ft_age.getText().trim()); String msg = "生日: " + date + "/r/n IP地址: " + ipadd + "/r/n 年令: " + age; javax.swing.JOptionPane.showMessageDialog(null, msg); } }; // 给输入框加上监听器当按下回车弹出消息框显示输入值,如输入不正确,则不会弹出!!! // ft_date.addActionListener(acton); // ft_ipAdd.addActionListener(acton); ft_age.addActionListener(acton); f.setSize(300, 100); f.setVisible(true); } 特别要注意的是:格式化输入时,如果用户输入内容不足,事件将不出发生。 1. 程序启动时,窗口第一次出现的位置总是在左上角,这不符合使用习惯,应当让它初始显示在屏的正中。如下代码所示,窗口初始化显示在屏的右下方:
public static void main(String args[]) { JFrame f = new JFrame("初始化窗口显示位置测试--显在屏右下"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //取得屏幕大小对象:dm的getWidth和getHeight返回屏幕的宽和高的double型值 java.awt.Dimension dm=java.awt.Toolkit.getDefaultToolkit().getScreenSize(); int sc_width=(int)dm.getWidth(); int sc_heigth=(int)dm.getHeight(); //组件对象的getWidth和getHeight方法得到自身的宽和高,int型 //组件的setLocation设置组件所处位置的x,y坐标: f.setLocation((sc_width-f.getWidth())/2,(sc_heigth-f.getHeight())/2); f.setSize(300, 100); f.setVisible(true); } | 你来改正?让我们的blog管理程序初始显示在屏的正中! 2. 界面功能的改进: 树形结构,一般用做导航的功能,就像操作系统的资源管理器打开时一样;具体数据的展示,如本例中,用户信息和用户的blog信息,应当放在JTable中展示,这样才符合常规;并且,完整的界面,应当还有菜单条和工具栏;请想想还有哪些不足,你还可改进哪些?在下一节,我们掌握如何在应用中结合使用JTree和JTable:使用JTree导航,使用JTable展示数据。 总结和任务: 1.实践:完成本节示例代码的编写; 2.实践:编写代码测试树节点的遍历,增删; 3.实践:自己编写树,实现简单的管理功能(如管理学生和学生的日志对象分别为枝节点和叶节点、管理部门和部门的员工对象分别为枝节点和叶节点. . .)。 4.总结:JTree使用总结: | |