[笔者]下面的文章,是一个网友在工作中遇到的编程问题与我一起探计的对话过程实录。希望有可以帮到初学者的地方。
[网友]
在网上看了你的几篇有关Java编程的文章,受益颇深。我是个初学者,有个很阳春的问题想请教一下:我用JBuilder写了这样一个程序,启动时展现一个窗口,点击该窗口上的一个按钮后,即冻结当前窗口,同时打开另一个窗口,在此时的窗口环境下处理完任务后,用dispose()撤销本窗口。这一步完成的很顺利,可是该怎样激活原来的窗口呢?我没辄了,请在百忙之中给予指点,多谢!
[笔者]
你在主窗口JForm中,启动一个模态的JDialog,则主窗口自动冻结,无法点击,当关闭对话框,主窗口又可以用了。这样就可以达到你的要求。
[网友]
谢谢你的指点。这是一种解决方案,但假如应用系统有特殊的要求,需要开启/锁定特定的窗口,又该如何解决呢?
另外有一个问题,我打算开发三层结构的系统,客户端用Java写应用窗口(不是浏览器形式),请问可以使用Jetty+JBoss的免费中间件作为信息分发机制吗?因为在我的印象里,Jetty和Tomcat都是Servlet引擎。
[笔者]
假设你的特定窗口为MyFrame,请看以下代码:
public class MainFrame extend JFrame
{
//重载setEnable()方法
public void setEnable(boolean bool)
{
Component comps[] = this.getComponents();
int num = comps.length();
for ( int i=0;i<num;i++)
{
try{
comps[i].setEnable(bool);
}catch(Exception ex){}
}
}
...
}
在你的特定窗口中重载setEnable()方法,将所有的子组件设置为bool,就可以达到目的。
用Jboss是一个不错的选择。特别是3.0,它已经实现了EJB2.0规范,已不仅仅是servlet容器了。
[网友]
非常感谢你的耐心指教。可是我无法实现这个重载过程,因为往这个类里加入这段代码时,总是出错。我用的是JBuilder 6,现将这段代码和包寄给你,麻烦你帮我看看该加在何处。是在是不好意思,给你添麻烦了。主窗口文件名叫LogonClass.java,第二个窗口的文件名叫ModifyPwdWindow.java。启动主窗口后,点击“修改密码”,弹出第二个窗口,此时主窗口失效。在第二个窗口上点击“取消”,该窗口被关闭,但无法激活主窗口。拜托了。
[笔者]
我已看过你的程序。像你这种对话框式的界面不应该设计成JFrame的,而应该是JDialog.所以,如果是JDialog的话,就可以很容易的用
JDialog dialog = new MyDialog(title,parent,true);
dialog.show();
类似以上的方式来实现你的目的。
而修改现有的程序为JDialog也不是很难,只要将extends JFrame改成extends JDialog,再将原来处理冻结的代码去掉就行了。
[网友]
陈先生,非常感谢你的指点,我的问题已经解决。保持联系,有空来厦门做客。我的电话(H)XXXXXXXX ,(M)XXXXXXXXXXX
[网友]
陈先生:再次麻烦你了。请问如何用Java设计报表并打印?前提是不使用任何第三方工具。我对此一无所知,望指点一二。可否帮忙给出一小段代码?多谢了。
[笔者]
设计报表本身就是一本复杂的技术,不然Crystal report,StyleReport就不会卖数万到数十万元了。
设计报表的出发点就是要看你的输出格式是什么。通常有pdf,excel,html等。在http://www.object-refinery.com有输出pdf和excel格式报表的开发包,你可以试一下。
[网友]
Hi 陈先生,谢谢指教。同时想问一下有关多文档(MDI)界面的开发问题。在JBuilder中,对于已有的Frame,可否手动将其改为Internal Frame?可否给出一小段代码介绍如何编写MDI应用程序?谢谢!
另外,对于已有的web服务器,如Jetty、Tomcat,可否用应用程序与之通信?而不是常见的浏览器存取形式?再次感谢。
[笔者]
在你的jdk目录中C:\j2sdk1.4.1\demo\jfc\Metalworks,有一个MDI界面的例子,我想这个例子比我写的代码会更有可学性。你现有的Frame可以直接把extends JFrame改成extends JInternalFrame,基本上就可以了。
如果App要与Web端通讯,我常用的方式是用Servlet,RMI据说也是较常用的方式,但我通常不用。你可以把你的数据封装成一个小型的Object,然后在Servlet中用GET/POST的方法,将你这个Object用ObjectInputStream和ObjectOutputStream的形式来传递。
[网友]
这里有我摘录的别人设计的一段代码,是关于MDI设计的。我的程序也类似这种情形。但我是在JBuilder中先将子窗体设计好,
然后再在如下形式的代码中创建该子窗体的实例,我认为应该是可以实现的。可不知为何不能成功。在JBuilder中,若加入一个JPanel
容器,然后将工具栏置入其中,此时若指定Jpanel为contentPane,则无法显示该工具栏。这是怎么回事?请赐教。谢谢!
/*
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JInternalFrames extends JFrame {
public static void main(String[] args) {
new JInternalFrames();
}
public JInternalFrames() {
super("Multiple Document Interface");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
content.setBackground(Color.white);
JDesktopPane desktop = new JDesktopPane();
desktop.setBackground(Color.white);
content.add(desktop, BorderLayout.CENTER);
setSize(450, 400);
for(int i=0; i<5; i++) {
JInternalFrame frame
= new JInternalFrame(("Internal Frame " + i),
true, true, true, true);
frame.setLocation(i*50+10, i*50+10);
frame.setSize(200, 150);
frame.setBackground(Color.white);
desktop.add(frame);
frame.moveToFront();
}
setVisible(true);
}
}
*/
[笔者]请先设置jpanel.setLayout(new BorderLayout()),再setContentPane()试一试,
如果可以,请告诉我一下。
[网友]陈先生:问题已解决,非常感谢!主要因为是初学,又要赶任务,实在是勉为其难。这几天感到收获不小。另外,如何限制子窗口的移动区域?比如限制其向工具栏的方向移动?
[笔者] 我想你需要继承一个DesktopManager,重载以下几个方法:
public void dragFrame(JComponent f, int newX, int newY)
public void endDraggingFrame(JComponent f)
public void beginResizingFrame(JComponent f, int direction)
因为JDesktopPane中的子窗口是由DesktopManager管理的,每次对子窗口的操作,会回调DesktopManager中的相应方法,
我想你只要重载关于拖动的几个方法就可以达到你的目的了。
[网友]在产生desktop对象后,JBuilder会自动生成一句:desktop.add(doc,Integer(5));我把其中的Integer(5)去掉后,程序依然能够运行,我不知道这个量起什么作用(ProWindowOperation.java中的第463行)。
借助您提供的方法,我利用菜单动作启动这个子窗口,即点击“人事管理”后打开它,然后使该菜单失效,当关闭该子窗口后再恢复该菜单的作用。这个动作是在子窗口的类中(SubMDIWindow.java第62行)实现的。这里有一个问题,就是必须在ProWindowOperation.java中(第27,28行)将“JMenu humanManagejMenu”定义成static型,否则就不能执行,我不得要领。
第三,您可能注意到我在窗口中已加入了一个状态条,如果我需要在该条的头部显示窗口开启信息,同时在尾部显示系统时间,该怎样处理呢?
第四,我今天找了本JAVA 2 Swing 卷2,有关DesktopManager的用法一时还无法理解,可否帮忙在给点明确的提示?谢谢!
[笔者]由于时间有限,有些问题我只告诉你解决的途径:
(1)Integer(5)是用来设置子窗口在Jdesktoppane中的层用的。具体你可以看JAVA 2 Swing 卷2中的JalyeredPane的讲解。
(2)为什么是static是因为它是内部类的原因,这个你可以看java的书籍中有关内部类的讲解。
(3)可以用第(4)条中的DesktopManager来实现。
(4)对子窗口的打开,关闭,拖动,Resize等操作,都是调用DesktopManager中的相应方法。这样的话你要扩展一个DesktopManager,实现自已的特定操作。如你要在子窗口开启和关闭时在状态条中有相应信息显示,你可以把这个状态条作为你的DesktopManager的构造函数的一个参数,传给你的DesktopManager。当你对子窗口开启或关闭时,就会调用你的DesktopManager的相应方法。也就是说,你要实现DesktopManager的openFrame()和closeFrame()方法,让它在开启或关闭时在状态条上显示信息。如:
public class MyDesktopManager extends DesktopManager
{
private 状态条 my状态条=null;
public MyDesktopManager(状态条 my状态条)
{
this.my状态条 = my状态条;
}
public void openFrame(JInternalFrame frame)
{
//设置my状态条的信息
......
}
public void closeFrame(JInternalFrame frame)
{
//设置my状态条的信息
......
}
}
具体的我就不写了。
至于显示系统时间,你可以使用一个Timer来每秒在状态条更新一次时间显示。
[网友]谢谢你的耐心,实在不好意思耽误你的时间。有了你的提示,我想我可以解决问题。这里又有一个问题:
在用鼠标点击或按热键ALT+Q后,激活系统退出菜单,按回车键弹出一个确认对话框,在这个对话框上有两个按钮:“是”与“否”。按“是”则退出,按“否”则回到系统工作区。问题是用鼠标点选这两个按钮,则毫无问题,可是,当用TAB键选中“否”后再按回车,则看到的效果依然是选中了“是”,仍然执行了退出命令。但按空格键时确是按期望的执行的。不知何故?一下是这段动作的代码:
void quitjMenu_keyPressed(KeyEvent e)
{
if(e.getKeyCode()==e.VK_ENTER)
{
quitjMenu.setSelected(false); //取消菜单选中状态
quitjMenu.setBackground(Color.lightGray);//恢复菜单背景色
//Font myFont=new Font("serif",Font.PLAIN,12);//建立新字体
// UIManager.put("OptionPane.font",new FontUIResource(myFont)); //1.修改对话框上的字体
//UIManager.put("OptionPane.font",new Font("serif",Font.PLAIN,12));//2.修改对话框上的字体
int result = JOptionPane.showConfirmDialog(this,"确定要退出系统吗?","系统退出确认",
JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.YES_OPTION )
System.exit(0);
else if(result == JOptionPane.NO_OPTION )
{
quitjMenu.setSelected(false);//取消菜单选中状态
quitjMenu.setBackground(Color.lightGray);//恢复菜单背景色
}
}
}
另外,我试图修改确认对话框上的字体,从SUN论坛上找了这么两句话,如上1,2的注释行。可是毫无效果,难道就不能修改吗?这个对话框上的字体实在是太难看了。请赐教。多谢!!
[笔者]我给你写了一个示例程序,
import javax.swing.*;
import java.awt.*;
import javax.swing.plaf.*;
public class test
{
public static void setUIFont (javax.swing.plaf.FontUIResource f)
{
java.util.Enumeration keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements())
{
Object key = keys.nextElement();
Object value = UIManager.get (key);
if (value instanceof javax.swing.plaf.FontUIResource)
UIManager.put (key, f);
}
}
public static void main(String[] args)
{
Font myFont=new Font("宋体",Font.ITALIC,18);
FontUIResource fontRes = new javax.swing.plaf.FontUIResource(myFont);
setUIFont(fontRes);
int result = JOptionPane.showConfirmDialog(null,
"Are you sure exit?",
"",
JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.YES_OPTION )
{
System.out.println("YES");
}
else if(result == JOptionPane.NO_OPTION )
{
System.out.println("NO");
}
}
}
设置字体我用的是自定义的setUIFont()方法。
用TAB键按空白键与按回车键结果不一样是因为Enter键已经绑定到dialog的default按钮(有焦点的按扭)上了,按回车当然是按的YES.