数据加载模糊进度指示面板的实现与应用(原)

数据加载模糊进度指示面板的实现与应用(原)
数据加载模糊进度指示面板的实现与应用
当在加载数据(或其它耗时工作)时,需要显示一个进度指示面板,本文介绍了一种简易的实现方式。(2009.11.30最后更新)

对于许多Swing应用,在与用户的交互过程中可能需要与数据库进行通信(如,加载数据)。而这个过程往往比较耗时,为了不造成"假死"现象,一般都会显示一个模糊进度指示器(不一定使用JProgressBar,简单地用一个图片代替即可),当数据加载完毕后,该进度指示器自动消失。
    一般地,该模糊进度指示器不会展示在一个弹出的对话框中(因为这样不美观),而是直接显示在需要展示被加载数据的面板中,并且对该面板进行模糊处理。实现这一功能的关键就在于,在屏幕的同一区域内展示两层面板:一层是展示数据的面板;另一层是展示进度指示器的面板。当加载数据时,显示进度指示器面板,并模糊数据面板;当数据加载完毕后,隐藏进度指示器面板,并使数据面板清晰显示。下面将使用org.jdesktop.swingx.StackLayout方式来实现上述功能。

1. LoadingPanel--加载指示器面板
    首先创建一个加载指示器面板。如前所述,我们不必使用真正的进度条作为进度指示器,仅需要使用一张动态图片来代替即可。LoadingPanel的完整代码如下所示,
public   class  LoadingPanel  extends  JPanel {

    
private   static   final   long  serialVersionUID  =   1962748329465603630L ;

    
private  String mesg  =   null ;

    
public  LoadingPanel(String mesg) {
        
this .mesg  =  mesg;
        initUI();
        interceptInput();
        setOpaque(
false );
        setVisible(
false );
    }

    
private   void  initUI() {
        JLabel label 
=   new  JLabel(mesg);
        label.setHorizontalAlignment(JLabel.CENTER);
        label.setIcon(
new  ImageIcon(getClass().getResource( " /path/to/spinner.gif " )));

        setLayout(
new  BorderLayout());
        add(label, BorderLayout.CENTER);
    }

    
private   void interceptInput() {
        addMouseListener(
new  MouseAdapter() {});
        addMouseMotionListener(
new  MouseMotionAdapter() {});
        addKeyListener(
new  KeyAdapter() {});

        addComponentListener(
new  ComponentAdapter() {
            @Override
            
public   void  componentShown(ComponentEvent e) {
                requestFocusInWindow();
            }
        });
        setFocusTraversalKeysEnabled(
false );
    }
}
上述代码很容易理解,LoadingPanel中仅有一个JLabel,它会展示一张图片(spinner.gif)及一段信息。但有两段代码需要特别说明:
[1]构造器中的两行代码

setVisible( false );
setOpaque(
false );
LoadingPanel只在加载数据时才显示,其它时候是不显示的,所以它默认不可见。另外,在显示LoadingPanel的同时,我们仍然希望能看到数据面板,所以LoadingPanel应该是透明的。
[2]interceptInput方法
当LoadingPanel显示之后,我们不希望用户还能够操作数据面板,那么就需要屏蔽掉用户(鼠标,键盘)输入。
addMouseListener( new  MouseAdapter() {});
addMouseMotionListener(
new  MouseMotionAdapter() {});
addKeyListener(
new  KeyAdapter() {});
上述三行代码就使得LoadingPanel能捕获所有的鼠标与键盘事件,并忽略掉它们。但仅仅如此还不够,在展示LoadingPanel时,数据面板中的某个UI组件很可能已经获得焦点了,那么用户仍然可以通过键盘操控数据面板中的组件(因为系统会把键盘事件发送给当前获取焦点的组件)。而且,即使数据面板中没有任何组件获得焦点,用户仍然可以通过Tab键把焦点转移到数据面板中的组件上。为了阻止这一操作,还需要加上如下几行代码,
addComponentListener( new  ComponentAdapter() {  //  一旦LoadingPanel可见,即获取焦点
    @Override
    
public   void  componentShown(ComponentEvent e) {
        requestFocusInWindow();
    }
});
setFocusTraversalKeysEnabled(
false );  //  阻止用户转移焦点

2. 示例程序

    在此处的示例程序中,数据面板(dataPanel)中仅有一个按钮,当点击该按钮时会显示loadingPanel,且模糊掉dataPanel,并会启动一个新的线程,该线程会在睡眠大约3秒(模拟耗时的数据加载工作)之后隐藏loadingPanel,且使dataPanel重新清晰可见。
    值得注意的是,该示例程序使用了SwingX中的两个组件:JXPanel和StackLayout。JXPanel提供了一个方法(setAlpha)以方便地设置Panel的透明度(Alpha值);而StackLayout允许在同一块区域内添加多层组件,并能同时展示所有层的组件(而,CardLayout一次只能显示某一层的组件)。完整的示例程序如下所示,
public   class  LoadDataDemo  extends  JFrame {

    
private   static   final   long  serialVersionUID  =   5927602404779391420L ;

    
private  JXPanel dataPanel  =   null //  使用org.jdesktop.swingx.JXPanel,以方便设置清晰度

    
private  LoadingPanel loadingPanel  =   null ;

    
public  LoadDataDemo() {
        
super ( " LoadData Demo " );
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        initUI();
    }

    
private   void  initUI() {
        JButton button 
=   new  JButton( " Load Data " );
        button.addActionListener(handler);
        dataPanel 
=   new  JXPanel( new  FlowLayout(FlowLayout.CENTER));
        dataPanel.add(button);

        loadingPanel 
=   new  LoadingPanel( " Loading " );

        
//  使用org.jdesktop.swingx.StackLayout,将loadingPanel置于dataPanel的上方
        JPanel centerPanel  =   new  JXPanel( new  StackLayout());
        centerPanel.add(dataPanel, StackLayout.TOP);
        centerPanel.add(loadingPanel, StackLayout.TOP);

        Container container 
=  getContentPane();
        container.setLayout(
new  BorderLayout());
        container.add(centerPanel, BorderLayout.CENTER);
    }

    
transient   private  ActionListener handler  =   new  ActionListener() {

        
public   void  actionPerformed(ActionEvent e) {
           
//  将dataPanel及其子组件的清晰度设置为50%;并显示loadingPanel
            dataPanel.setAlpha( 0.5F );
            loadingPanel.setVisible(
true );

            Thread thread 
=   new  Thread() {
                
public   void  run() {
                    
try  {
                        Thread.sleep(
3000L );  //  睡眠约3秒钟,以模拟加载数据的过程
                    }  catch  (InterruptedException e) {
                        e.printStackTrace();
                    }
                   
//  数据加载完毕后,重新隐藏loadingPanel;并使dataPanel及其子组件重新清晰可见
                    loadingPanel.setVisible( false );
                    dataPanel.setAlpha(1F);
                };
            };
            thread.start();
        }
    };

    
public   static   void  main(String[] args) {
        LoadDataDemo demo 
=   new  LoadDataDemo();
        demo.setSize(
new  Dimension( 400 300 ));
        demo.setVisible(
true );
    }
}

3. 不使用SwingX

    SwingX为我们提供了一系列功能强大,使用简易的Swing扩展组件,我强烈建议你去使用它。但若因故,你不准备使用它时,我们仍然有替代的解决方案,但此处仅简述一二。
[1]对于设置Alpha值,需要创建一个继承自JPanel的DataPanel类,覆写paintComponent方法,在其中使用Alpha合成,
Graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
[2]对于StackLayout,我们可以使用GlassPane(玻璃窗格)或LayeredPane(分层窗格)进行替换,将LoadingPanel设置为GlassPane或LayeredPanel中的一层。由于一个JFrame只有一个GlassPane,为了程序的灵活性,一般首选使用LayeredPane。

你可能感兴趣的:(数据加载模糊进度指示面板的实现与应用(原))