John Jiang

          a cup of Java, cheers!
          https://github.com/johnshajiang/blog

             :: 首頁 ::  :: 聯系 :: 聚合  :: 管理 ::
            131 隨筆 :: 1 文章 :: 530 評論 :: 0 Trackbacks
          數據加載模糊進度指示面板的實現與應用
          當在加載數據(或其它耗時工作)時,需要顯示一個進度指示面板,本文介紹了一種簡易的實現方式。(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中的兩個組件:JXPanelStackLayout。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(400300));
                  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。

          posted on 2009-11-29 20:33 John Jiang 閱讀(2199) 評論(5)  編輯  收藏 所屬分類: JavaSEJavaSwing原創

          評論

          # re: 數據加載模糊進度指示面板的實現與應用(原) 2009-12-01 12:18 創意禮品
          非常好的文章,謝謝樓主分享!非常好的文章,謝謝樓主分享!  回復  更多評論
            

          # re: 數據加載模糊進度指示面板的實現與應用(原) 2009-12-01 20:12 BeanSoft
          兄弟 這種情況我個人覺得用 JRootPane 會更好一些.

          不過我的代碼沒考慮鍵盤的事件 呵呵 很早以前自己搞的玩意了

          package beansoft.swing;

          import javax.swing.*;

          import java.awt.BorderLayout;
          import java.awt.Cursor;
          import java.awt.FlowLayout;
          import java.awt.event.*;

          /**
          * We have to provide our own glass pane so that it can paint a loading
          * dialog and then the user can see the progress but can't operation
          * the GUI, it's a transparent pane so the below contents is visible.
          *
          * Also please refer to articles by Sun - How to Use Root Panes:
          * {@link http://java.sun.com/docs/books/tutorial/uiswing/components/rootpane.html}
          * @author Jacky Liu
          * @version 1.0 2006-08
          */
          public class LoadingGlassPane extends JPanel {
          private static final long serialVersionUID = 1L;
          /**
          * A label displays status text and loading icon.
          */
          private JLabel statusLabel = new JLabel("Reading data, please wait...");

          public LoadingGlassPane() {
          try {
          statusLabel.setIcon(new ImageIcon(getClass().getResource(
          "loading.gif")));
          } catch (RuntimeException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
          }

          statusLabel.setHorizontalAlignment(JLabel.CENTER);

          // Must add a mouse listener, otherwise the event will not be
          // captured
          this.addMouseListener(new java.awt.event.MouseAdapter() {
          public void mousePressed(MouseEvent e) {
          }
          });

          this.setLayout(new BorderLayout());

          this.add(statusLabel);
          // Transparent
          setOpaque(false);
          }

          public JLabel getStatusLabel() {
          return statusLabel;
          }

          /**
          * Set the text to be displayed on the glass pane.
          *
          * @param text
          */
          public void setStatusText(final String text) {
          SwingUtilities.invokeLater(new Runnable() {
          public void run() {
          statusLabel.setText(text);
          }
          });
          }

          /**
          * Install this to the jframe as glass pane.
          * @param frame
          */
          public void installAsGlassPane(JFrame frame) {
          frame.setGlassPane(this);
          }

          /**
          * A small demo code of how to use this glasspane.
          * @param args
          */
          public static void main(String[] args) {
          JFrame frame = new JFrame("Test GlassPane");
          final LoadingGlassPane glassPane = new LoadingGlassPane();
          glassPane.installAsGlassPane(frame);

          JButton button = new JButton("Test Query");

          button.addActionListener(new ActionListener() {

          public void actionPerformed(ActionEvent e) {
          // Call in new thread to allow the UI to update
          Thread th = new Thread() {
          public void run() {
          glassPane.setVisible(true);
          glassPane.setCursor(new Cursor(Cursor.WAIT_CURSOR));
          // TODO Long time operation here
          try {
          Thread.sleep(2000);
          } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          }
          glassPane.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
          glassPane.setVisible(false);
          }
          };

          th.start();
          }

          });

          frame.getContentPane().setLayout(new FlowLayout());
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.getContentPane().add(button);

          frame.setSize(200, 200);
          frame.setVisible(true);

          }

          }  回復  更多評論
            

          # re: 數據加載模糊進度指示面板的實現與應用(原) 2009-12-02 08:50 Sha Jiang
          @BeanSoft
          我瀏覽了一下你的代碼,你使用的方法就是我在文章末尾提到的"將LoadingPanel設置為GlassPane..."這種方法。
          一般地,若要使用GlassPane時,最好優先考慮使用LayeredPane。
          但我必須要說,使用StackLayout最靈活性,也最簡單。  回復  更多評論
            

          # re: 數據加載模糊進度指示面板的實現與應用(原) 2009-12-20 01:25 Krista31
          Click <a href="http://www.4writers.net">freelance writing </a> when you want get some knowledge just about this topic.   回復  更多評論
            

          # re: 數據加載模糊進度指示面板的實現與應用(原) 2011-02-11 21:14 外貿女裝批發
          正好有一朋友問到這個問題,就借大作獻佛了。呵呵。多謝。~~  回復  更多評論
            

          主站蜘蛛池模板: 甘肃省| 通河县| 新宾| 松溪县| 出国| 怀远县| 昌都县| 新竹市| 科技| 杭锦后旗| 天柱县| 绥滨县| 宜良县| 印江| 五莲县| 黔西县| 新竹县| 宿松县| 长泰县| 济宁市| 吉林市| 绥棱县| 交口县| 渭南市| 东明县| 蛟河市| 尼玛县| 武川县| 南江县| 广水市| 磴口县| 荆州市| 碌曲县| 无为县| 麻栗坡县| 柏乡县| 沾化县| 兴义市| 通山县| 连平县| 宜昌市|