[原创]Swing技巧8:完美的LookAndFeel解决方案

类别:Java 点击:0 评论:0 推荐:
[原创]Swing技巧8:完美的LookAndFeel解决方案 Swing技巧8:完美的LookAndFeel解决方案


Swing技巧4:设置系统窗口,边框,标题,最小化,还原,最大化按钮

Swing技巧5:运行中重设LookAndFeel
中,我使用
if(currentLookAndFeel.getSupportsWindowDecorations()){
   frame.setUndecorated(true);
   frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
}

public static void setLookAndFeel(Component target, LookAndFeel lnf){
    try{
        UIManager.setLookAndFeel(lnf);
        SwingUtilities.updateComponentTreeUI(target);
    }catch(Exception e){
        System.out.println("ERROR: " + e);
    }
}
达到不但设置LookAndFeel,更使系统窗口,边框,标题,最小化,还原,最大化按钮也有LookAndFeel的目的.

但此种方法缺点也很明显:
1.Matrix网友sslazio21反映新弹出的JFileChooser的系统窗口边框任为System LookAndFeel
2.对没有系统窗口,边框,标题的LookAndFeel,比如Windows,Motif及他们的子类,根JFrame也就没有系统窗口,边框,标题,导致无法最小化,还原,最大,改变大小.

1的解决方法是我在Swing技巧4:设置系统窗口,边框,标题,最小化,还原,最大化按钮中提到的方法1:全局设置
//JFrame,JDialog.setDefaultLookAndFeelDecorated
//static global setting
//Provides a hint as to whether or not newly created JDialogs and JFrames should have their Window decorations
//(such as borders, widgets to close the window, title...) provided by the current look and feel. 
JFrame.setDefaultLookAndFeelDecorated(true);


完整的初始化是
try{
    Class lafClass=Class.forName(currentLookAndFeel);
    LookAndFeel laf=(LookAndFeel)(lafClass.newInstance());
    JFrame.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
    JDialog.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
}catch(Exception e){}

必须对JFrame和JDialog都设置.

注意frame.setUndecorated(true)和JFrame.setDefaultLookAndFeelDecorated(true)必须在窗口显示之前,也就是frame.show()或frame.setVisible(true)之前.
因为setUndecorated的java doc说This method can only be called while the frame is not displayable.
setDefaultLookAndFeelDecorated的java doc说newly created JDialogs and JFrames,对新建窗口有效.

对于2,我希望窗口,边框,标题的LookAndFeel随着setLookAndFeel而更改,如果是对没有窗口,边框,标题的LookAndFeel那么就使用System的系统窗口,边框,标题.
除了前面的初始化以外,我还改一下setLookAndFeel:
public static void setLookAndFeel(Component target, LookAndFeel lnf){
    try{
        JFrame.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
        JDialog.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
        UIManager.setLookAndFeel(lnf);
        SwingUtilities.updateComponentTreeUI(target);
    }catch(Exception e){
        System.out.println("ERROR: " + e);
    }
}
对新建窗口(新弹出的JFileChooser)是有效了,但对已经存在的窗口,边框,标题不会改变.
看来对已经存在的窗口,必须使用
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
但有setUndecorated:This method can only be called while the frame is not displayable.
试试
frame.hide();
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
frame.show();
不但没有效果,在setLookAndFeel时还失去响应.

查java doc发现the frame is not displayable的状态只有在frame的第一次show()之前和dispose()以后.
只有dispose()以后了:
frame.dispose();
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
frame.show();
这次在setLookAndFeel时正常退出了.

再查发现java.awt.Window.dispose:When the last displayable window within the Java virtual machine (VM) is disposed of, the VM may terminate.
就是说最后一个窗口dispose后JVM可能退出.
玩个小花招,不让它是the last displayable window:
Frame temp=new Frame();
temp.show();
temp.hide();
frame.dispose();
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
frame.show();
temp.dispose();
成功了,达到我希望的效果:窗口,边框,标题的LookAndFeel随着setLookAndFeel而更改,如果是对没有窗口,边框,标题的LookAndFeel那么就使用System的系统窗口,边框,标题.

这样可以说是Swing LookAndFeel的完美解决方案,最终代码如下:

初始化时,第一个窗口建立之前:
try{
    Class lafClass=Class.forName(currentLookAndFeel);
    LookAndFeel laf=(LookAndFeel)(lafClass.newInstance());
    JFrame.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
    JDialog.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
}catch(Exception e){}

public static void setLookAndFeel(JFrame frame, LookAndFeel lnf){
    try{
        //改变全局设置
        JFrame.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
        JDialog.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
        UIManager.setLookAndFeel(lnf);
        
        //改变当前frame的窗口,边框,标题
        Frame temp=new Frame();
        temp.show();
        temp.hide();
        frame.dispose();
        frame.setUndecorated(lnf.getSupportsWindowDecorations());
        frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
        frame.show();
        temp.dispose();

        SwingUtilities.updateComponentTreeUI(frame);
    }catch(Exception e){
        System.out.println("ERROR: " + e);
    }
}
很不幸,对JDialog必须也有一个,虽然只改了一个单词,还是要有.
public static void setLookAndFeel(JDialog frame, LookAndFeel lnf){
    try{
        //改变全局设置
        JFrame.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
        JDialog.setDefaultLookAndFeelDecorated(laf.getSupportsWindowDecorations());
        UIManager.setLookAndFeel(lnf);
        
        //改变当前frame的窗口,边框,标题
        Frame temp=new Frame();
        temp.show();
        temp.hide();
        frame.dispose();
        frame.setUndecorated(lnf.getSupportsWindowDecorations());
        frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
        frame.show();
        temp.dispose();

        SwingUtilities.updateComponentTreeUI(frame);
    }catch(Exception e){
        System.out.println("ERROR: " + e);
    }
}

参考资料:
1.Java Docs
2.Swing技巧5:运行中重设LookAndFeel,来自SwingSet2源码
3.Swing技巧4:设置系统窗口,边框,标题,最小化,还原,最大化按钮,中第二种方法,来自BeanBuilder源码

PS:
1.用此种方法改造的SwingSet2:
『 点击下载 』
可以用ant编译.
只改了SwingSet2.java文件,大家可以用Windiff(Vc6中有)或WinMerge与原始文件(在%Java_home%\demo\jfc\SwingSet2\SwingSet2.jar\src\中)比较.
第三方LookAndFeel,可以到 www.javootoo.com 下载,放在%Java_home%\jre\lib\ext\中.中

2.对setLookAndFeel中的temp,更好应该是在程序开始时
Frame temp=new Frame();
temp.show();
temp.hide();
结束时
temp.dispose();
效果一样,但节约资源,setLookAndFeel中就可以直接
frame.dispose();
frame.setUndecorated(true);
frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
frame.show();
不过,这样很容易忘了temp,导致无法退出,必须使用System.exit().

3.为什么对JFrame和JDialog,有一个完全相同的setLookAndFeel?
因为Swing的继承体系:
         Window
        /   |         Frame |  Dialog
       |    |     |
       | JWindow  |
       |          |
     JFrame   JDialog
setUndecorated只有JFrame和JDialog中有.
很奇怪的继承体系,不是吗?JFrame和JDialog也应该是JWindow的子类,这样才符合直觉.但java是单根继承的,sun又该死的弄了两套gui,awt和swing.

本文地址:http://com.8s8s.com/it/it11345.htm